以下讨论提供了几个 MySQL 检测函数依赖关系的方法示例。示例使用以下符号:The following discussion provides several examples of the ways in which MySQL detects functional dependencies. The examples use this notation:
{X} -> {Y}
理解这句话为“X 唯一决定 Y,” 这也意味着 Y 在功能上依赖于 X。Understand this as “X uniquely
determines Y,” which also
means that Y is functionally
dependent on X.
示例使用的是 world 数据库,可以从 https://dev.mysql.com/doc/index-other.html 下载。您可以在同一页面找到有关如何安装数据库的详细信息。The examples use the world database, which
can be downloaded from
https://dev.mysql.com/doc/index-other.html. You can find details
on how to install the database on the same page.
以下查询选取每个国家的语言数量:The following query selects, for each country, a count of spoken languages:
SELECT co.Name, COUNT(*) FROM countrylanguage cl, country co WHERE cl.CountryCode = co.Code GROUP BY co.Code;
co.Code 是 co 的主键,因此 co 的所有列都函数依赖于它,用这种符号表示:co.Code is a primary key of
co, so all columns of co
are functionally dependent on it, as expressed using this
notation:
{co.Code} -> {co.*}
因此,co.name 函数依赖于 GROUP BY 列,并且该查询是有效的。Thus, co.name is functionally dependent on
GROUP BY columns and the query is valid.
可以使用在 UNIQUE 列上的 UNIQUE 索引代替主键,并且相同的函数依赖仍然适用。(对于允许 NULL 值的 NOT
NULL 索引,这不成立,因为它允许多个 NULL 值,这种情况下唯一性会丧失。)A UNIQUE index over a NOT
NULL column could be used instead of a primary key
and the same functional dependence would apply. (This is not
true for a UNIQUE index that permits
NULL values because it permits multiple
NULL values and in that case uniqueness is
lost.)
该查询为每个国家选择所有使用的语言及讲这些语言的人数列表:This query selects, for each country, a list of all spoken languages and how many people speak them:
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 的所有列:The pair (cl.CountryCode,
cl.Language) is a two-column composite
primary key of cl, so that column pair
uniquely determines all columns of cl:
{cl.CountryCode, cl.Language} -> {cl.*}
此外,由于 WHERE 子句中的等式:Moreover, because of the equality in the
WHERE clause:
{cl.CountryCode} -> {co.Code}
并且,因为 co.Code 是 co 的主键:And, because co.Code is primary key of
co:
{co.Code} -> {co.*}
“唯一确定” 关系是可传递的,因此:“Uniquely determines” relationships are transitive, therefore:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
因此,该查询是有效的。As a result, the query is valid.
与前面的例子一样,可以在 NOT NULL 列上使用 UNIQUE 键代替主键。As with the previous example, a UNIQUE key
over NOT NULL columns could be used instead
of a primary key.
可以使用 INNER JOIN 条件代替 WHERE。相同的函数依赖性适用:An INNER JOIN condition can be used instead
of WHERE. The same functional dependencies
apply:
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 条件中的相等测试是对称的,但外连接条件中的相等测试不是对称的,因为表扮演的角色不同。Whereas an equality test in a WHERE
condition or INNER JOIN condition is
symmetric, an equality test in an outer join condition is not,
because tables play different roles.
假设参照完整性意外被破坏,并且存在一行 countrylanguage 数据而在 country 中没有对应行。考虑与前一个示例相同的查询,但使用 LEFT JOIN:Assume that referential integrity has been accidentally broken
and there exists a row of countrylanguage
without a corresponding row in country.
Consider the same query as in the previous example, but with a
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 决定)。在每种情况下,这种关系适用:For a given value of cl.CountryCode, the
value of co.Code in the join result is
either found in a matching row (determined by
cl.CountryCode) or is
NULL-complemented if there is no match
(also determined by cl.CountryCode). In
each case, this relationship applies:
{cl.CountryCode} -> {co.Code}
cl.CountryCode 本身函数依赖于 {cl.CountryCode, cl.Language},这是一个主键。cl.CountryCode is itself functionally
dependent on {cl.CountryCode,
cl.Language} which is a primary key.
如果在连接结果中 co.Code 被 NULL 补全,则 co.Name 也会被补全。如果 co.Code 没有被 NULL 补全,那么由于 co.Code 是主键,它决定了 co.Name。因此,在所有情况下:If in the join result co.Code is
NULL-complemented,
co.Name is as well. If
co.Code is not
NULL-complemented, then because
co.Code is a primary key, it determines
co.Name. Therefore, in all cases:
{co.Code} -> {co.Name}
结果如下:Which yields:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
因此,该查询是有效的。As a result, the query is valid.
但是,假设表格被交换,如在此查询中:However, suppose that the tables are swapped, as in this query:
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;
现在这种关系 不适用:Now this relationship does not apply:
{cl.CountryCode, cl.Language} -> {cl.*,co.*}
实际上,为 cl 创建的所有 NULL 补充行都被放入单个组中(它们的 GROUP BY 列都等于 NULL),并且在该组内 co.Name 的值可以变化。查询无效,MySQL 会拒绝它。Indeed, all NULL-complemented rows made for
cl is put into a single group (they have
both GROUP BY columns equal to
NULL), and inside this group the value of
co.Name can vary. The query is invalid and
MySQL rejects it.
因此,外连接中的函数依赖性与决定列是否属于 LEFT JOIN 的左侧或右侧相关。如果存在嵌套的外连接或连接条件不完全是等值比较,则函数依赖性的确定会更加复杂。Functional dependence in outer joins is thus linked to whether
determinant columns belong to the left or right side of the
LEFT JOIN. Determination of functional
dependence becomes more complex if there are nested outer
joins or the join condition does not consist entirely of
equality comparisons.
假设一个关于国家的视图会生成它们的代码、它们的大写名称以及它们有多少种不同的官方语言:Suppose that a view on countries produces their code, their name in uppercase, and how many different official languages they have:
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;
这个定义是有效的,因为:This definition is valid because:
{co.Code} -> {co.*}
在视图结果中,第一个选中的列是 co.Code,它也是分组列,因此决定了所有其他选中的表达式:In the view result, the first selected column is
co.Code, which is also the group column and
thus determines all other selected expressions:
{country2.Code} -> {country2.*}
MySQL 理解这一点,并按照以下描述使用这些信息。MySQL understands this and uses this information, as described following.
此查询显示各个国家的官方语言数量以及城市数量,通过将视图与 city 表连接实现:This query displays countries, how many different official
languages they have, and how many cities they have, by joining
the view with the city table:
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;
这个查询是有效的,因为,如前所见:This query is valid because, as seen previously:
{co2.Code} -> {co2.*}
MySQL 能够在视图的结果中发现一个函数依赖,并利用该依赖来验证使用该视图的查询。如果 country2 是一个派生表(或公共表表达式),情况也是一样,如下所示:MySQL is able to discover a functional dependency in the
result of a view and use that to validate a query which uses
the view. The same would be true if
country2 were a derived table (or common
table expression), as in:
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;