以下讨论提供了几个 MySQL 检测函数依赖关系的示例。这些示例使用以下符号:
{X} -> {Y}
将其理解为 “X
唯一确定 Y
”, 这也意味着 Y
函数依赖于 X
。
这些示例使用 world
数据库,可以从 https://dev.mysqlserver.cn/doc/index-other.html 下载。您可以在同一页面上找到有关如何安装数据库的详细信息。
以下查询为每个国家/地区选择使用的语言数量:
SELECT co.Name, COUNT(*)
FROM countrylanguage cl, country co
WHERE cl.CountryCode = co.Code
GROUP BY co.Code;
co.Code
是 co
的主键,因此 co
的所有列都函数依赖于它,使用以下符号表示:
{co.Code} -> {co.*}
因此,co.name
函数依赖于 GROUP BY
列,并且查询有效。
可以使用 NOT NULL
列上的 UNIQUE
索引代替主键,并且适用相同的函数依赖关系。(对于允许 NULL
值的 UNIQUE
索引,情况并非如此,因为它允许多个 NULL
值,在这种情况下,唯一性将丢失。)
此查询为每个国家/地区选择所有使用语言的列表以及使用人数:
SELECT co.Name, cl.Language,
cl.Percentage * co.Population / 100.0 AS SpokenBy
FROM countrylanguage cl, country co
WHERE cl.CountryCode = co.Code
GROUP BY cl.CountryCode, cl.Language;
对 (cl.CountryCode
, cl.Language
) 是 cl
的两列复合主键,因此该列对唯一确定 cl
的所有列:
{cl.CountryCode, cl.Language} -> {cl.*}
此外,由于 WHERE
子句中的等式:
{cl.CountryCode} -> {co.Code}
并且,因为 co.Code
是 co
的主键:
{co.Code} -> {co.*}
“唯一确定” 关系是可传递的,因此:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
因此,查询有效。
与前面的示例一样,可以使用 NOT NULL
列上的 UNIQUE
键代替主键。
可以使用 INNER JOIN
条件代替 WHERE
。适用相同的函数依赖关系:
SELECT co.Name, cl.Language,
cl.Percentage * co.Population/100.0 AS SpokenBy
FROM countrylanguage cl INNER JOIN country co
ON cl.CountryCode = co.Code
GROUP BY cl.CountryCode, cl.Language;
虽然 WHERE
条件或 INNER JOIN
条件中的相等性测试是对称的,但外部联接条件中的相等性测试不是对称的,因为表扮演着不同的角色。
假设引用完整性已被意外破坏,并且 countrylanguage
中存在一行在 country
中没有对应行。考虑与前一个示例相同的查询,但使用 LEFT JOIN
:
SELECT co.Name, cl.Language,
cl.Percentage * co.Population/100.0 AS SpokenBy
FROM countrylanguage cl LEFT JOIN country co
ON cl.CountryCode = co.Code
GROUP BY cl.CountryCode, cl.Language;
对于给定的 cl.CountryCode
值,联接结果中 co.Code
的值要么在匹配行中找到(由 cl.CountryCode
确定),要么在没有匹配项时为 NULL
补充(也由 cl.CountryCode
确定)。在每种情况下,此关系都适用:
{cl.CountryCode} -> {co.Code}
cl.CountryCode
本身函数依赖于 {cl.CountryCode
, cl.Language
},它是主键。
如果在联接结果中 co.Code
为 NULL
补充,则 co.Name
也是如此。如果 co.Code
不是 NULL
补充的,那么因为 co.Code
是主键,所以它决定了 co.Name
。因此,在所有情况下:
{co.Code} -> {co.Name}
这将产生:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
因此,查询有效。
但是,假设交换了表,如下面的查询所示:
SELECT co.Name, cl.Language,
cl.Percentage * co.Population/100.0 AS SpokenBy
FROM country co LEFT JOIN countrylanguage cl
ON cl.CountryCode = co.Code
GROUP BY cl.CountryCode, cl.Language;
现在,这种关系不适用:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
实际上,为 cl
创建的所有 NULL
补充行都放在一个组中(它们的两个 GROUP BY
列都等于 NULL
),并且在这个组中,co.Name
的值可能会有所不同。查询无效,MySQL 会拒绝它。
因此,外部联接中的函数依赖关系与决定列是属于 LEFT JOIN
的左侧还是右侧相关。如果存在嵌套的外部联接或联接条件不完全由相等比较组成,则函数依赖关系的确定会变得更加复杂。
假设国家/地区的视图生成其代码、大写的名称以及它们有多少种不同的官方语言:
CREATE VIEW country2 AS
SELECT co.Code, UPPER(co.Name) AS UpperName,
COUNT(cl.Language) AS OfficialLanguages
FROM country AS co JOIN countrylanguage AS cl
ON cl.CountryCode = co.Code
WHERE cl.isOfficial = 'T'
GROUP BY co.Code;
此定义有效,因为:
{co.Code} -> {co.*}
在视图结果中,第一个选择的列是 co.Code
,它也是分组列,因此决定了所有其他选择的表达式:
{country2.Code} -> {country2.*}
MySQL 理解这一点并使用此信息,如下所述。
此查询显示国家/地区、它们有多少种不同的官方语言以及它们有多少个城市,方法是将视图与 city
表联接:
SELECT co2.Code, co2.UpperName, co2.OfficialLanguages,
COUNT(*) AS Cities
FROM country2 AS co2 JOIN city ci
ON ci.CountryCode = co2.Code
GROUP BY co2.Code;
此查询有效,因为如前所述:
{co2.Code} -> {co2.*}
MySQL 能够发现视图结果中的函数依赖关系,并使用该依赖关系来验证使用该视图的查询。如果 country2
是派生表(或公用表表达式),情况也是如此,如下所示:
SELECT co2.Code, co2.UpperName, co2.OfficialLanguages,
COUNT(*) AS Cities
FROM
(
SELECT co.Code, UPPER(co.Name) AS UpperName,
COUNT(cl.Language) AS OfficialLanguages
FROM country AS co JOIN countrylanguage AS cl
ON cl.CountryCode=co.Code
WHERE cl.isOfficial='T'
GROUP BY co.Code
) AS co2
JOIN city ci ON ci.CountryCode = co2.Code
GROUP BY co2.Code;