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