14.19.4 函数依赖检测
以下讨论提供了 MySQL 检测函数依赖的多种示例。这些示例使用以下记法:
{X} -> {Y}
理解为““X
唯一确定 Y
,””,也就是说 Y
对于 X
是函数依赖。
这些示例使用了名为 world
的数据库,可以从https://dev.mysql.com/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
列,并且查询是有效的。
可以使用 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
的 pair (cl.CountryCode
, cl.Language
) 是一个二列主键,因此该列对唯一确定 cl
的所有列:
{cl.CountryCode, cl.Language} -> {cl.*}
此外,因为 WHERE 子句中的等式:
{cl.CountryCode} -> {co.Code}
而且,因为 co.Code
是 co
的主键:
{co.Code} -> {co.*}
“唯一确定” 关系是传递的,因此:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
因此,查询是有效的。
与前一个示例一样,可以使用UNIQUE
键来代替主键,这个键覆盖了NOT NULL
列。
可以将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
值,join结果中的co.Code
值是 either 在匹配行中找到(由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 将 reject 它。
外连接中的函数依赖性因此与左或右侧的确定列相关联。如果有嵌套的外连接或连接条件不完全是等式比较,那么函数依赖性的确定将变得更加复杂。
假设一个关于国家的视图生产它们的代码、它们的名称(大写)和官方语言的数量:
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 understands this and uses this information, as described following.
这个查询显示了国家、它们的官方语言数量和城市数量,通过将视图与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;
MySQL 可以组合所有前述类型的函数依赖关系(键基于、等值基于、视图基于)来验证更复杂的查询。