Реляционная модель: таблицы, строки, колонки

Реляционная модель: таблицы, строки, колонки

В прошлом уроке мы разобрались, чем база данных принципиально отличается от россыпи CSV или JSON: она навязывает структуру, следит за типами, умеет искать и согласовывать данные между собой. Теперь пора заглянуть внутрь — посмотреть, как именно устроены данные в самой популярной разновидности баз. Эта разновидность называется реляционной, и именно с ней мы будем работать весь курс. Если ты раньше видел Excel-таблицу, ты уже на 70% знаком с тем, что увидишь дальше. Оставшиеся 30% — это как раз то, ради чего реляционные базы существуют, и мы их аккуратно разберём.

Зачем нам отдельная «модель», если есть просто файлы

Вспомни боль из урока 1-1: десятки CSV-файлов, в одном из них поле email написано как e_mail, в другом — как EmailAddress, у одного клиента три записи в customers.csv, у второго — две, и никто не помнит, какая из них «правильная». Когда данных мало, файлов хватает. Когда их становится много и они начинают ссылаться друг на друга, нужен общий язык: способ договориться, как именно мы храним информацию, как описываем «одну сущность» и как соединяем сущности между собой.

Этот общий язык и называется моделью данных. Модель — это набор правил и понятий, которыми мы пользуемся, чтобы описывать предметную область: клиентов, заказы, товары, события. Моделей в мире несколько (есть документные, есть графовые, есть «ключ-значение»), но самая распространённая, проверенная временем и стоящая за большинством бизнес-систем — реляционная модель. Её придумал Эдгар Кодд в 1970 году, и с тех пор она в том или ином виде живёт почти во всех «серьёзных» базах данных.

Главная мысль реляционной модели звучит обманчиво просто: данные хранятся в виде таблиц. Но за этим стоит цепочка строгих идей: что такое таблица, что такое строка, что такое колонка, и почему между ними есть жёсткие правила, которых нет в Excel. Давай разбираться по слоям.

Таблица — это «отношение»

В реляционной модели таблица называется красивым словом отношение (по-английски — relation, отсюда и название модели). Не пугайся слова: оно не про «отношения» в человеческом смысле, а про математическое понятие — некий набор фактов одной и той же формы. Например, отношение «пользователи» — это набор фактов вида «у пользователя есть идентификатор, имя, email и дата регистрации». Каждый такой факт — отдельная запись в этом наборе.

Когда мы говорим «таблица users», мы имеем в виду именно отношение, описывающее пользователей. Один из канонических разделов в документации PostgreSQL формулирует это так: «Таблица в реляционной базе данных похожа на таблицу на бумаге: у неё есть строки и столбцы. Число и порядок столбцов фиксированы, у каждого столбца есть имя. Число строк переменное — оно отражает, сколько данных хранится в данный момент». Заметь две важные вещи. Во-первых, столбцы фиксированы — мы заранее договариваемся, какая информация хранится. Во-вторых, строки переменны — их может быть ноль, десять или десять миллионов, и это нормально.

Вот как это выглядит на практике. Представим таблицу users нашего учебного приложения:

idnameemailcreated_at
1Аннаanna@example.com2024-03-12
2Борисboris@example.com2024-04-01
3Викаvika.dev@example.com2024-04-17
4Глебgleb@example.com2024-05-02

Это — отношение. Четыре столбца с заранее объявленными именами и смыслами, и четыре строки, каждая из которых рассказывает про одного конкретного пользователя.

Анатомия: строки и колонки

Теперь по частям. У реляционной таблицы два «измерения»:

  • колонка (она же column, она же атрибут в академической терминологии) — это вертикальный срез: одно конкретное свойство, общее для всех записей. У нашей users четыре колонки: id, name, email, created_at. У каждой колонки есть имя и тип (об этом чуть ниже). Колонка задаёт вопрос, на который каждая строка должна ответить: «как зовут?», «когда зарегистрировался?».
  • строка (она же row, она же кортежtuple — в академической терминологии) — это горизонтальный срез: одна конкретная запись, один конкретный факт, один пользователь. Строка обязана содержать значения для всех колонок, которые описаны в схеме (или явное «значения нет», но об этом — в более поздних уроках).

В формальной терминологии реляционной модели говорят: «кортеж — это набор из n значений, где n — это степень отношения, и каждое значение в кортеже соответствует уникальному атрибуту». Расшифровка: «строка состоит ровно из стольких значений, сколько колонок объявлено в таблице, и каждое значение принадлежит своей колонке». Это не свобода Excel, где в одну ячейку можно ввести что угодно — это строгое правило, которое база данных проверяет за тебя.

И ещё одно важное свойство, которое обычно становится сюрпризом для новичков: порядок строк в реляционной таблице не определён. Если ты смотришь на таблицу users и видишь Анну сверху, а Глеба снизу, это не значит, что «так оно и хранится». База имеет право показывать строки в любом порядке, если ты явно не попросишь её отсортировать. Тот же раздел документации PostgreSQL прямо предупреждает: «SQL не даёт никаких гарантий относительно порядка строк в таблице». Запомни этот тезис — он отличает реляционные таблицы от списков и от Excel-листов, где порядок ячеек — часть смысла.

Проверь себя. Если в таблице users поменять местами строки Анны и Бориса, изменится ли смысл данных? — Нет. Та же самая таблица, та же самая информация. Смысл несут значения в строках, а не их визуальный порядок.

Схема и данные — это разные вещи

Реляционная модель аккуратно разделяет два уровня:

  • схема — это «рецепт» таблицы. Имя таблицы, список её колонок, имя и тип каждой колонки, ограничения (например, «email обязателен» или «id уникален»). Схема меняется редко, она описывает форму данных.
  • данные — это сами строки внутри таблицы. Они меняются постоянно: пользователи регистрируются, что-то обновляют, что-то удаляют.

Это очень похоже на разницу между бланком документа и заполненными бланками. Бланк (схема) — один, и в нём заранее напечатано: «Имя: ___», «Дата рождения: ___». Заполненных бланков (строк) могут быть тысячи, и все они одинаковой формы. Если завтра ты захочешь добавить новое поле в бланк — это изменение схемы, и оно затронет все будущие записи разом. Если ты просто записал нового пользователя — это изменение данных, схема осталась прежней.

Из академического определения реляционной модели: «Схема задаёт имена отношений, их заголовки и ограничения, которые должны выполняться для каждого экземпляра базы данных». Перевод на человеческий: схема — это контракт между тобой и базой о том, как выглядят данные. База будет проверять каждую новую запись на соответствие этому контракту.

Тип колонки: дисциплина, которой нет в Excel

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

  • целые числаid пользователя, количество товара в заказе.
  • числа с дробной частью — сумма заказа, цена.
  • текст — имя, email, описание.
  • даты и моменты времени — когда пользователь зарегистрировался, когда сделан заказ.
  • логические значения — «подтверждён ли email», «активен ли пользователь».

Важный момент: тип колонки одинаков для всех строк. Если колонка created_at объявлена как «дата и время», то ни в одной строке там не появится текст «вчера утром». Документация PostgreSQL поясняет смысл типа так: «Тип данных ограничивает множество возможных значений, которые можно присвоить колонке, и придаёт смысл хранимым данным, чтобы их можно было использовать в вычислениях». Иначе говоря, тип нужен не только для проверки, но и чтобы база понимала, что вообще можно делать со значением: складывать его, сравнивать как дату, искать как текст.

Сравни с Excel: там в одну колонку «дата» легко проскальзывает строка «не помню точно», и формулы перестают работать. Реляционная база такой строки просто не примет — она отклонит запись с осмысленной ошибкой ещё до того, как «грязные» данные попадут внутрь. Это и есть та самая дисциплина, которую мы получаем взамен на чуть более строгие правила игры.

(В этом курсе мы пока намеренно говорим о типах общо — «текст», «число», «дата». Конкретные имена типов разнятся между разными СУБД, и мы вернёмся к ним детально в более поздних модулях.)

Уникальная строка и идея «первичного ключа»

Допустим, у нас в users появились два Бориса с одинаковыми именами и одинаковыми email — что делать? Как мы поймём, какой из них тот самый, к которому относится сегодняшний заказ? Реляционная модель предлагает простое и сильное решение: у каждой строки должен быть способ её однозначно опознать.

Для этого выбирается одна или несколько колонок, чьи значения не повторяются от строки к строке. Такая колонка называется первичный ключ (primary key). В нашей таблице это колонка id: значения 1, 2, 3, 4 — все разные, и каждое из них указывает ровно на одного пользователя. Канонически: «первичный ключ — это ключ-кандидат, выбранный по соглашению как предпочтительный уникальный идентификатор кортежей». Без паники: на этом этапе тебе достаточно понять идею — у каждой строки есть «адрес», по которому к ней можно вернуться.

Зачем нужен такой адрес? Главным образом — чтобы:

  • обращаться к конкретной строке («покажи мне пользователя с id = 3»),
  • обновлять её, не задевая остальные («у пользователя id = 3 поменялся email»),
  • ссылаться на неё из других таблиц — об этом следующий раздел.

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

Связи между таблицами: один-ко-многим

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

Представим, что наши пользователи делают заказы. Заказ — это отдельная сущность: у него есть свой идентификатор, своё содержимое, своя сумма. Завести колонку «заказы» прямо внутри таблицы users плохо: у одного пользователя может быть и ноль, и десять, и сто заказов, мы просто не уместим их в одну ячейку. Поэтому в реляционной модели заказы живут в отдельной таблице, а связь с пользователем хранится в виде ссылки на его id:

iduser_idproductamount
101Курс «SQL basics»4900
112Курс «SQL basics»4900
121Курс «JS basics»5900
133Подписка Pro1490
141Доп. модуль990

Прочитай эту таблицу вместе с предыдущей. Колонка user_id в orders — это не имя пользователя и не его email. Это значение первичного ключа id из таблицы users. Строка orders.id = 10 сообщает: «заказ номер 10, который сделал пользователь с id = 1, на сумму 4900». Глядя в users, мы видим, что id = 1 — это Анна. Значит, заказ принадлежит Анне.

У одного пользователя может быть несколько строк в orders (Анна сделала три заказа: 10, 12 и 14). У одного заказа — ровно один пользователь. Такая связь называется один-ко-многим: «один пользователь — много заказов». Это самый частый и самый базовый вид связи в реляционных базах. Есть и другие виды (например, многие-ко-многим), но к ним мы вернёмся в отдельном модуле.

Важно усвоить идею, а не имя. Идея: вместо того чтобы дублировать данные о пользователе в каждой записи о заказе, мы храним пользователя один раз — в users, а в orders держим только короткую ссылку на его id. Если Анна изменит email, нам не придётся переписывать пять её заказов: email живёт в одной строке users, а заказы продолжают спокойно ссылаться на её id.

Проверь себя. В таблице orders выше — сколько разных пользователей сделали заказы и сколько всего заказов у Анны? Подсказка: посмотри на колонку user_id.

Типичные заблуждения новичков

Когда люди впервые слышат «таблицы, строки, колонки», у них в голове чаще всего всплывает Excel. Это нормально и даже полезно как стартовая аналогия, но у неё есть три ловушки, в которые легко попасть:

  • «Порядок строк — это часть данных». Нет. В реляционной таблице строки — множество, а не список. Если тебе важно показать данные в каком-то порядке (например, сначала самые новые регистрации), ты должен явно попросить базу отсортировать. Без явного запроса порядок не гарантируется, даже если он выглядит стабильным.
  • «Таблица — это лист Excel». Похоже, но строже. В Excel в одну колонку можно вписать число, в другую — текст, в третью — пустоту, и никто не возмутится. В реляционной таблице каждый столбец имеет свой тип, и значения, не подходящие под тип, просто не примутся. И ещё: в Excel ячейка может содержать формулу, которая считается «на лету», а реляционная таблица хранит готовые значения (вычисляемые поля бывают, но это отдельная тема и сейчас не наша забота).
  • «Колонки можно добавлять прямо во время работы, как удобно». Можно, но это изменение схемы — то есть контракта, на который опирается весь остальной код. Такие изменения делаются осознанно и обычно — отдельным шагом, а не «на ходу». В Excel ты просто пишешь в колонку H первое попавшееся слово, в реляционной базе так делать нельзя: колонки H там нет, пока ты её явно не объявил.

Если ты будешь помнить эти три отличия, ментальный переход от Excel к реляционным таблицам пройдёт почти безболезненно.

Что унести из урока

  • Реляционная модель — это способ описывать данные как набор таблиц (отношений) со строгими правилами.
  • В таблице есть колонки (фиксированные свойства с типом) и строки (отдельные записи). Строка обязана соответствовать форме, заданной колонками.
  • Схема (рецепт таблицы) и сами данные — разные вещи. Схема меняется редко, данные — постоянно.
  • Каждая колонка имеет тип, и этот тип одинаков для всех строк. Тип защищает от мусора и придаёт значениям смысл.
  • У каждой строки должен быть способ её опознать — первичный ключ. Через него же другие таблицы ссылаются на неё, образуя связи (например, один-ко-многим).

Мост к следующему уроку

Мы поняли, как реляционная модель устроена концептуально. Но как именно говорить с базой данных на эту тему — какими словами просить «покажи мне всех пользователей» или «добавь заказ»? Для этого существует специальный язык — SQL, и у него есть несколько диалектов, которые тонко отличаются друг от друга. Об этом — в уроке 1-3.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике