Spec-Zone.ru › MySQL 9.2

14.19.4 Обнаружение функциональной зависимости

В приведенном ниже обсуждении представлены несколько примеров способов, которыми MySQL обнаруживает функциональные зависимости. В примерах используется такая запись:

{X} -> {Y}

Понимайте это как “X однозначно определяет Y,” что также означает, что Y функционально зависит от X.

В примерах используется база данных world, которую можно загрузить с https://dev.mysql.com/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, и запрос является корректным.

Индекс UNIQUE по столбцу NOT NULL можно использовать вместо первичного ключа, и та же функциональная зависимость будет применяться. (Это неверно для индекса UNIQUE, который допускает NULL значения, так как он допускает несколько 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.*}

В результате запрос является корректным.

Как и в предыдущем примере, ключ 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.*}

Действительно, все строки, являющиеся дополнениями NULL, добавленные для cl, помещаются в одну группу (у них оба столбца 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 может объединять все вышеперечисленные типы функциональных зависимостей (основанные на ключах, на равенстве, на представлениях) для проверки более сложных запросов.

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-8.4-en/group-by-functional-dependence.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API