Основы реляционных баз данных
2026-02-26 22:47 Diff

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

Нормальная форма

Возьмем для примера интернет-магазин, в котором продается электроника. Когда пользователь делает заказ, в базу данных заносится запись об этом. В нее входит вся необходимая информация: данные пользователя, какой товар он купил, сколько он стоил и адрес доставки. Затем эти данные используются всеми подразделениями интернет-магазина — от бухгалтеров до службы доставки.

Таблица order_items

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

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

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

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

Первая нормальная форма

Первая нормальная форма сводится к трем правилам:

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

Разберем каждое правило подробнее.

Каждая ячейка – одно значение

Вернемся к примеру выше. У одной записи поля item и price содержат два значения через запятую. У такого способа организации данных много недостатков. Например, пропадает возможность делать обычную выборку по условиям:

Другая проблема связана с типами данных. Поле price в таблице order_items имеет числовой тип (numeric). Если мы захотим хранить там более одного значения, то тип превратится в строковый, а все данные станут обычными строками.

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

Чтобы избавиться от перечислений в ячейках, можно создать новые записи:

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

Данные одного типа

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

Разные данные в рамках одного поля тоже не дают выполнить агрегирующие запросы, например, поиск сумм, максимального, минимального. Еще усложняется обработка данных на уровне кода. В коде придется каким-то образом понимать, что из себя представляют данные.

Вот еще несколько примеров с похожей ситуацией:

  • Хранение даты свадьбы в поле «день рождения»
  • Хранение номера телефона вместо адреса в поле «адрес»

Исправленная версия таблицы:

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

Уникальные записи

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

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

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

Лучше добавить первичный ключ (PRIMARY KEY) — поле или набор полей, которые содержат уникальное значение для каждой записи. Первичный ключ не может меняться, его значение однозначно определяет любую запись в таблице.

Разберем два вида первичного ключа:

  • Естественный — когда используются значения из окружающего мира, например, email, ФИО или паспортные данные. При этом нужно убедиться, что ключ не будет повторяться. Такие первичные ключи используют редко из-за их ненадежности. Часто они не уникальны и могут изменяться или повторяться. Например, номер паспорта меняется при смене документа
  • Суррогатный — когда используются автоматически генерируемые уникальные значения. Такой ключ поддерживается любой базой данных «из коробки». Иногда это просто числа, а иногда — сложные число-буквенные строки или хеши

Добавим в нашу таблицу первичный ключ:

Первичный ключ принято создавать первым полем с названием id. Для первичного ключа обязательно указывать PRIMARY KEY в описании таблицы:

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

Выводы

В этом уроке мы узнали, что такое нормальная форма в реляционной модели. Она помогает облегчить считываемость информации в таблице. Еще мы подробно разобрали первый уровень формы. Так мы узнали, что каждая ячейка таблицы может хранить только одно значение. Иначе может произойти путаница с типами данных, из-за чего будет невозможно проверить их корректность.

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