文档主页
MySQL 9.0 参考手册
相关文档 下载本手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 9.0 参考手册  /  ...  /  函数依赖关系检测

14.19.4 函数依赖关系检测

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

{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.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 补充行都放在一个组中(它们的两个 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;

函数依赖关系的组合

MySQL 能够结合所有前面类型的函数依赖关系(基于键、基于相等、基于视图),以验证更复杂的查询。