以下讨论提供了几个 MySQL 检测函数依赖关系的示例。这些示例使用以下符号:
{X} -> {Y}将其理解为 “X 唯一确定 Y”, 这也意味着 Y 函数依赖于 X。
这些示例使用 world 数据库,可以从 https://dev.mysqld.com.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;