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.