文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  函数依赖检测

14.19.4 函数依赖检测

以下讨论提供了 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.Codeco 的主键,因此 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.Codeco 的主键

{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.CodeNULL 补全的,则 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;

函数依赖的组合

MySQL 能够组合所有上述类型的函数依赖(基于键、基于相等性、基于视图)来验证更复杂的查询。