Skip to content

Функциональные зависимости

Функциональные зависимости — это основа нормализации баз данных.

Под функциональной зависимостью подразумевается зависимость значения одного атрибута от другого.

Если даны два атрибута А и Б некоторого отношения, то говорят, что Б функционально зависит от А, если в любой момент времени каждому значению А соответствует ровно одно значение Б.

Если возьмем любую таблицу, то значения в кортеже функционально зависят от значений первичного ключа этой таблицы.

Избыточные ФЗ

Избыточной зависимостью называют ситуацию, при которой зависимость заключает информацию, которая может быть получена из других зависимостей.

Правила вывода зависимостей применяются к списку функциональных зависимостей с целью избавиться от избыточных зависимостей.

Набор функциональных зависимостей, получаемый из исходного набора функциональных зависимостей удалением всех избыточных функциональных зависимостей с помощью правила вывода, называется минимальным покрытием.

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

Пример

За основу возьмем таблицу со следующими атрибутами:

unit_id, emp_id, fio

Здесь можно выделить следующие функциональные зависимости:

unit_id -> emp_id и emp_id -> fio, то есть unit_id функционально определяет emp_id, а emp_id функционально определяет fio.

Можно записать как F = {unit_id -> emp_id, emp_id -> fio}.

Отношение R(A, B, C)

Давайте немного изменим обозначения и представим отношение в следующем виде:

R(A, B, C)

Есть отношение R(A, B, C), в котором есть следующие функциональные зависимости: F = {A -> B, B -> C}.

Если F представляет собой набор функциональных зависимостей, то замыкание F, обозначаемое как F+, представляет собой набор всех функциональных зависимостей, логически подразумеваемых F.

Можем предположить следующие логически возможные зависимости, которые могут существовать: F+ = {A -> C, A -> A, B -> B, C -> C}.

Количество возможных первичных ключей

Давайте разберем, какие могут быть первичные ключи в данной таблице, которые будут функционально определять значения в кортеже.

Комбинации ключейКоличество ключей в комбинации
A1
B1
C1
AB1
AC1
BC1
ABC1
Итого7

То есть всего может быть 7 вариантов первичных ключей в данной таблице.

Если бы в отношении было больше атрибутов, то и количества вариантов первичных ключей было бы гораздо больше. Точное количество можно определить по формуле получения количества всех возможных комбинаций:

[ \frac{n!}{(n-r)! \cdot r!} ]

где r — количество атрибутов в ключе, а n — количество атрибутов в отношении.

Для отношения из 10 атрибутов будет 1023 комбинации первичных ключей.

Таким образом, должен быть какой-то механизм или инструмент, который позволит быстро определить те потенциальные первичные ключи, которые должны быть в отношении. Такой механизм в 1974 году предложил Уильям Армстронг.

Аксиомы Армстронга

Аксиомы Армстронга — это набор правил (правила вывода), которые при многократном применении генерируют замыкание функциональных зависимостей.

  • Рефлексивность
  • Пополнение
  • Транзитивность

Рефлексивность

Если В является подмножеством А, то А функционально определяет В.

Данная зависимость является тривиальной: правая часть таких зависимостей содержится в левой. То есть А -> В.

Пример: emp_id -> fio.

Пополнение

Если А функционально определяет В, то АС функционально определяет ВС. То есть если А -> В, то АС -> ВС.

Доказательство от обратного:
Представим, что зависимость АС -> ВС не соблюдается.

unit_idfio
A1C1
A2C2
emp_idfio
B1C1
B2C2

Пусть А1С1 = А2С2, но В1С1 != В2С2.
По аксиоме рефлексивности из первого равенства А1С1 = А2С2 следует, что А1 = А2. Так как А функционально определяет В, то В1 = В2, и из неравенства В1С1 != В2С2 следует, что С1 != С2. Но это нарушает тривиальную зависимость АС -> С.
Таким образом, мы доказываем, что зависимость АС -> ВС должна соблюдаться.

Транзитивность

Если А функционально определяет В и В функционально определяет С, то А функционально определяет С.
То есть если А -> В и В -> С, то А -> С. Такая зависимость является избыточной и должна быть устранена.

unit_idemp_id
A1B1
A2B2
emp_idfio
B1C1
B2C2

Доказательство от обратного:
Предположим, что зависимость А -> С не соблюдается: А1 = А2, но С1 != С2.
Из наличия зависимости А -> В следует, что В1 = В2, а потому из наличия зависимости В -> С следует, что С1 = С2. Значит, предположение об отсутствии функциональной зависимости А -> С не является верным.

Правила вывода

Потенциально выводимая функциональная зависимость из замыкания может быть выведена с применением Аксиом Армстронга, при этом Аксиомы гарантируют отсутствие возможности вывода необходимых и нужных зависимостей, но несмотря на это были добавлены дополнительные правила вывода:

  • Самодетерминированность
  • Декомпозиция
  • Объединение
  • Композиция
  • Накопление

Самодетерминированность

Когда А -> А, что прямо следует из аксиомы Рефлексивности.

Декомпозиция

Если А -> ВС, то А -> В и А -> С. Согласно рефлексивности следует, что ВС -> В, согласно транзитивности следует, что А -> В. Таким же образом из ВС -> С и транзитивности следует зависимость А -> С.

Объединение

Если А -> В и А -> С, то А -> ВС. Согласно аксиоме Пополнения следует, что А -> АВ и АВ -> ВС, а согласно транзитивности следует, что А -> ВС.

Композиция

Когда А -> В и С -> D, то АС -> BD. Согласно пополнения получим зависимость АС -> ВС и ВС -> BD, а применив транзитивность получим, что АС -> BD.

Накопление

Если А -> ВС и В -> D, то А -> ВСD. Применяя пополнение, получим, что ВС -> ВСD, и применяя транзитивность, получим, что А -> ВСD.

Вывод

С помощью функциональных зависимостей можно накладывать на реляционную базу дополнительные ограничения.
Основной идеей является то, что значением одного атрибута в кортеже однозначно определяется значение другого атрибута.
Чем выше форма нормализации, тем меньше функциональных зависимостей может существовать.


Нормализация

Нормализация — это метод проектирования базы данных, который используется для разработки таблицы реляционной базы данных до более высокой нормальной формы.

При этом данный процесс является прогрессивным, и более высокий уровень нормализации базы данных не может быть достигнут, если не были выполнены предыдущие уровни.

Как правило, 3НФ является желаемым результатом, и дальнейшая нормализация может приводить к не нужному результату, из-за которого усложняется выборка данных.

Первая нормальная форма (1НФ)

Первая нормальная форма (1НФ) — основа нормализации данных. Таблица считается приведенной к 1НФ, если выполняются следующие условия:

  • Все значения таблицы являются атомарными, то есть ячейки таблицы должны содержать одиночные значения, и все записи в отдельном столбце таблицы должны иметь один и тот же тип данных. Одна ячейка — одно значение.
  • В качестве значений атрибута таблицы не допускаются группы или массивы повторяющихся данных.

Пример приведения к 1НФ

Исходная таблица пользователей:

user_namepassportaddress
Иванов Иван Иванович4508 785231
Петров Семен Семенович4724 254965
Федоров Максим Петрович5746 420983

Чтобы нормализовать данную таблицу и привести к 1НФ, нужно все значения сделать атомарными. Чтобы не хранить в рамках одной таблицы несколько сущностей, данные по городам и адресам вынесем в отдельные таблицы:

Таблица пользователей:

last_namefirst_namemiddle_nameserial_passnumber_passaddress_idcity_id
ИвановИванИванович450878523111
ПетровСеменСеменович472425496522
ФедоровМаксимПетрович574642098333

Таблица городов:

city_idcity_name
1Москва
2Саратов

Таблица адресов:

address_idstreethouse
1Проспект Мира17
2Елисейские поля1
3тупик Последний2

Таким образом, все три новые таблицы соответствуют требованиям 1НФ, так как значения в них атомарны.

Вторая нормальная форма (2НФ)

Чтобы таблица соответствовала 2 нормальной форме (2НФ), она должна быть в 1НФ. 2НФ говорит о том, что все значения в кортеже зависят от значения первичного ключа. Если в таблице составной первичный ключ, то значения в кортеже должны зависеть от всего состава первичного ключа, а не от его части.

Пример приведения к 2НФ

Дана таблица, которая находится в 1НФ и хранит информацию по проектам:

project_manager_idproject_idproject_namestart_dateend_date
17Рога с копытами01.05.202324.08.2023
225Просто без точки10.11.202331.12.2023

В данной таблице составной первичный ключ из атрибутов project_manager_id и project_id.
Столбец project_name зависит только от идентификатора проекта и не имеет отношения к руководителю проекта.

Нормализуем данную таблицу, убрав атрибут project_name, который не принадлежит таблице.

Таблица проектов и менеджеров:

project_manager_idproject_idstart_dateend_date
1701.05.202324.08.2023
22510.11.202331.12.2023

Таблица проектов:

project_idproject_name
7Рога с копытами
25Просто без точки

Теперь наборы значений в кортежах в двух таблицах зависят полностью от своих первичных ключей.

Третья нормальная форма (3НФ)

Чтобы БД соответствовала требованиям 3НФ, она должна находиться в 1НФ и 2НФ. Таблица находится в 3НФ, когда в таблице отсутствуют избыточные транзитивные функциональные зависимости. То есть отсутствуют атрибуты, которые зависят от неключевых атрибутов.

Пример приведения к 3НФ

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

project_idproject_nameproject_manager_idpm_address
1some_project_17some_address_1
2some_project_224some_address_2

Так как первичным ключом является столбец project_id, то все неключевые атрибуты в таблице должны зависеть от данного атрибута, но столбец pm_address зависит от столбца project_manager_id, который не имеет ограничения первичного ключа, то есть столбец pm_address не принадлежит данной таблице.

Нормальная форма Бойса-Кодда (НФБК)

НФБК — нормальная форма Бойса-Кодда. Достигается, когда отношение находится в 1НФ, 2НФ и 3НФ. НФБК — это расширенная и более строгая 3НФ. Если в таблице простой первичный ключ (один атрибут имеет данное ограничение), то НФБК является 3НФ.

Отношение находится в НФБК, когда каждая нетривиальная и неприводимая слева функциональная зависимость обладает потенциальным ключом в качестве детерминанта.

Пример приведения к НФБК

Возьмем таблицу по слушанию судебных дел, где есть номер зала заседания, номер статьи, ФИО судьи и тип слушания:

hall_numberarticlejudgetype_of_hearing
1171Иванов Иванзакрытое
1516Петров Петрзакрытое
2126Семенов Михаилоткрытое

Предположим, что статьи делятся на две категории, которые могут проходить только в открытом режиме и только в закрытом режиме. По статьям 171 и 516 могут быть только закрытые слушания, а по 126 — только открытые. Если составить список всех потенциально возможных составных первичных ключей, то получим:

  • [hall_number, article]
  • [hall_number, judge]
  • [type_of_hearing, judge]
  • [judge, article]

В данном случае все атрибуты входят в какой-либо из составных первичных ключей, то есть требования 2НФ соблюдены. Транзитивные функциональные зависимости отсутствуют, значит таблица находится в 3НФ. Но также существует функциональная зависимость между статьей и типом слушания, где тип слушания не может являться первичным ключом для статьи, значит таблица не соответствует требованиям НФБК.

Проблема будет заключаться в том, что для любой статьи можно указать любой тип слушания, что является аномалией модификации.

Проведем декомпозицию на два отношения:

Таблица слушаний:

hall_numberarticlejudge
1171Иванов Иван
1516Петров Петр
2126Семенов Михаил

Таблица типов слушаний по статьям:

articletype_of_hearing
171закрытое
516закрытое
126открытое

Теперь между нашими атрибутами отсутствуют какие-либо неявные зависимости, а значит таблицы находятся в соответствии с требованиями НФБК.

Четвертая нормальная форма (4НФ)

Отношение соответствует требованиям 4НФ, когда соответствует всем предыдущим НФ и в таблице отсутствуют многозначные зависимости. Таблица должна иметь как минимум три столбца (допустим, А, В и С), которые формируют составной первичный ключ. При этом В и С между собой никак не связаны и не зависят друг от друга, но по отдельности зависят от А, и для каждого значения А есть множество значений В, а также множество значений С. Данную многозначную зависимость можно записать как А→В, А→С.

Пример приведения к 4НФ

Возьмем таблицу, в которой фиксируется, какой сотрудник в каком магазине совершил продажу товара:

staff_idstore_idproduct_id
1287
1154
2152

Так как здесь составной первичный ключ из трех столбцов, то данная таблица автоматически находится в НФБК. Но из-за многозначной зависимости сотрудник → магазин и сотрудник → товар таблица нарушает требования 4НФ. То есть сотрудник может продавать в разных магазинах и может продавать разный товар. А в каком магазине будет продан товар и будет ли товар продан именно в этом магазине — не важно. Аномалия заключается в том, что при продаже нового товара можно указать ложный магазин.

Чтобы привести данную таблицу к 4НФ, сделаем декомпозицию на две таблицы:

Таблица «Сотрудник-Магазин»:

staff_idstore_id
12
11
21

Таблица «Сотрудник-Товар»:

staff_idproduct_id
187
154
215
21
252

Теперь есть отдельная таблица, в которой мы храним информацию о том, в каких магазинах работает сотрудник, и таблица, в которой храним информацию о том, какой товар был продан сотрудником. Так как в двух таблицах отсутствуют многозначные зависимости и в них составные первичные ключи, данные таблицы находятся в 4НФ.

Пятая нормальная форма (5НФ)

Чтобы получить 5НФ, нужно разделять таблицы на более малые таблицы для устранения избыточности данных. Разбиение идет до тех пор, пока нельзя будет воссоздать оригинальную таблицу путем объединения всех малых таблиц. Требование 5НФ заключается в том, чтобы в таблице каждая нетривиальная зависимость соединения определялась потенциальным ключом этой таблицы.

Пример

Возьмем предыдущий пример, когда мы нормализовали таблицы до 4НФ. Была исходная таблица в НФБК:

staff_idstore_idproduct_id
1287
1154
2152

После декомпозиции получили 2 малые таблицы, которые находятся в 4НФ:

staff_store:

staff_idstore_id
12
11
21

staff_product:

staff_idproduct_id
187
154
215
21
252

Как отдельные самостоятельные таблицы они решают поставленные задачи, но воссоздать первоначальную таблицу уже невозможно из-за ложных JOIN-ов по неуникальным значениям.

В данном случае применяется приведение таблиц к 5НФ, когда данные нужно декомпозировать по «цикличной» зависимости, то есть staff_id → store_id, store_id → product_id и staff_id → product_id. Необходимо создать еще одну таблицу, которая будет хранить данные о том, в каком магазине какой товар был продан:

product_store:

product_idstore_id
872
541
152
11
522

После нормализации до 5НФ таблицы staff_store, staff_product, product_store допустимо соединять только три вместе; использовать по две таблицы нельзя.


Денормализация

Денормализация — это процесс ухода от правил нормализации там, где это необходимо.

Для процесса денормализации не существует стандартного алгоритма. Процесс денормализации индивидуален и требует четкого понимания, для чего он необходим, несмотря на появление избыточности.

К денормализации прибегают для сокращения времени обработки запросов и уменьшения затрат ресурсов. В нормализованных базах часто приходится соединять большое количество таблиц или добавлять агрегацию.

Таким образом, денормализацию можно выполнить, сократив количество таблиц или добавив новые столбцы в существующую таблицу. При этом, учитывая избыточность данных, необходимо следить за целостностью данных.

Особенности в разных СУБД

В зависимости от того, в какой РСУБД происходит процесс денормализации, используются различные алгоритмы.

  • PostgreSQL: можно использовать материализованные представления.
  • MySQL (где отсутствуют материализованные представления): необходимо создавать денормализованную таблицу, которую придется наполнять через триггеры и единую процедуру. На все таблицы, которые участвуют в денормализации и вычислениях, накладываются триггеры, которые будут запускать одну и ту же процедуру, что будет гарантировать корректность и целостность данных.
  • Если в РСУБД отсутствуют материализованные представления и триггеры, тогда целостность при денормализации достигается единой функцией на уровне приложения.

Медленно меняющиеся измерения (SCD)

Медленно меняющиеся измерения (от англ. Slowly Changing Dimensions, SCD) — механизм отслеживания изменений в данных измерения в терминах хранилища данных.

Применяется в случае, если данные меняются не очень часто и не по расписанию.

Пример таких данных: ФИО пользователя, адрес контрагента, дата рождения, дата продажи и так далее. Иногда изменение таких данных может привести к потере целостности при работе с историческими данными.

Постановка проблемы

Представим ситуацию: есть данные о продаже:

payment_idcustomer_idstaff_idstore_idamount
1711500
21012700

Есть данные по сотрудникам:

staff_idfiostore_id
1Иванов Петр1
2Максим Сергеевич2

Насколько текущие данные позволяют отследить перевод сотрудника 1 в магазин 2?

Типы SCD

Тип 0 (SCD0) — Нулевой тип

Пассивный метод, предполагается, что значения атрибутов такого типа не будут меняться.

customer_idfioborn_dateborn_citypassport
1Иванов Петр01.05.1974Самара4477 050607

Здесь born_date и born_city — данные, которые никогда не изменятся, но ФИО или данные паспорта могут быть изменены.

Тип 1 (SCD1) — Первый тип

Использует простое перезаписывание: данные в таблице полностью заменяются на новые. Историчность при этом полностью теряется.

customer_idfioborn_dateborn_citypassport
1Семенов Петр01.05.1974Самара5588 323344

После того как пользователь поменял ФИО и паспорт, данные изменились, но мы потеряли то, что было изначально. Если какие-то данные были привязаны к паспорту (кредиты, сертификаты и т.д.), эти связи будут потеряны.

Тип 2 (SCD2) — Второй тип

Использует добавление новой строки и дополнительных столбцов. Позволяет сохранить историчность.

customer_idfioborn_dateborn_citypassportactivecreated_date
1Иванов Петр01.05.1974Самара4477 050607false01.01.2020
1Семенов Петр01.05.1974Самара5588 323344true17.06.2021

Сохраняется историчность данных, но данные начинают создавать избыточность. Чтобы контролировать, какая из записей актуальна, добавляются служебные столбцы active и created_date для версионности.

Тип 3 (SCD3) — Третий тип

Использует добавление новых столбцов-атрибутов, хранящих предыдущее значение для поддержания историчности. SCD2 позволяет хранить любое количество изменений, а SCD3 — ровно столько, сколько заранее отведено под это столбцов.

Исходное состояние:

customer_idfiopassportold_fioold_passportchange_date
1Иванов Петр4477 050607nullnullnull

После изменения:

customer_idfiopassportold_fioold_passportchange_date
1Семенов Петр5588 323344Иванов Петр4477 05060717.06.2021

Историчность сохранена, но пользователь не может больше менять ФИО или паспорт (или для этого потребуется добавлять новые столбцы).

Тип 4 (SCD4) — Четвертый тип

Использует отдельную таблицу для хранения изменений, а основная таблица содержит текущие данные.

Основная таблица (после изменений):

customer_idfioborn_dateborn_citypassportactual_date
1Семенов Петр01.05.1974Самара5588 32334417.06.2021

Таблица с изменениями:

customer_idfiopassportactual_date
1Иванов Петр4477 05060701.01.2020

Таким образом, основная таблица всегда содержит актуальную информацию, а при необходимости использования исторических данных можно присоединить таблицу с изменениями.

Выбор типа SCD

Какой именно тип использовать (или возможно сочетание типов) зависит от конкретной задачи и структуры данных.

При этом нужно помнить про такие инструменты, как представления, секционирование, вычисляемые столбцы, триггеры и хранимые процедуры, которые позволяют держать данные в нормализованном виде и хранить историчность с сохранением целостности данных.