以下讨论提供了 MySQL 检测函数依赖的方式的几个示例。这些示例使用以下符号
{X} -> {Y}
将此理解为 “X
唯一确定 Y
,” 这也意味着 Y
函数依赖于 X
。
这些示例使用 world
数据库,可从 https://dev.mysqlserver.cn/doc/index-other.html 下载。您可以在同一页面上找到有关如何安装数据库的详细信息。
以下查询为每个国家选择 spoken 语言的数量
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
值,在这种情况下唯一性会丢失。)
此查询为每个国家选择所有 spoken 语言的列表以及有多少人说这些语言
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.*}
因此,查询有效。
与前面的示例一样,可以使用 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
的给定值,连接结果中的 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
补全行都放入单个组中(它们都具有等于 NULL
的两个 GROUP BY
列),并且在这个组内 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;