# ITMO DB lesson 2

21 slides, by Olga1507Rub. https://easeweb.host/movie/lesson-2/1/

## Slide 1. Университет ИТМО · Школа разра
(id 53, https://easeweb.host/movie/lesson-2/1/)
- [button "← занятие 1"] -> presentation "ITMO DB Lesson 1" (lesson-1)
- Университет ИТМО · Школа разработки видеоигр · 11.03.04 ТРКИ
- Проектирование
  реляционной БД
- Занятие 2. Модели данных, ER-моделирование, нормальные формы
- Блок 1. Хранилища данных · Санеев Святослав Олегович · 2026

## Slide 2. План занятия
(id 54, https://easeweb.host/movie/lesson-2/2/)
- План занятия
- [card] 1 · Уровни модели данных · Концептуальная, логическая, физическая -> slide 3 "УРОВНИ ПРОЕКТИРОВАНИЯ"
- [card] 2 · Ключи и связи · Первичный, внешний, суррогатный 1:1, 1:N, N:M -> slide 6 "КЛЮЧИ"
- [card] 3 · ER-модель · Как из описания игры получить сущности и связи -> slide 10 "ПОРЯДОК РАБОТЫ"
- [card] 4 · Нормальные формы · 1НФ, 2НФ, 3НФ и когда их нарушают -> slide 12 "С ЧЕГО НАЧИНАЕТСЯ БЕДА"

## Slide 3. УРОВНИ ПРОЕКТИРОВАНИЯ
(id 55, https://easeweb.host/movie/lesson-2/3/)
- УРОВНИ ПРОЕКТИРОВАНИЯ
- Концептуальная
- Логическая
- Физическая
- Что вообще есть в игре: игроки, матчи, предметы. Никаких таблиц и типов — только сущности и связи между ними.
- Те же сущности, но уже в терминах реляционной модели: таблицы, ключи, связи.
- Конкретная СУБД: типы данных, индексы, размещение, ограничения. Здесь появляется PostgreSQL.
- «У игрока есть инвентарь»
- inventory(player_id, item_id, quantity)
- quantity INTEGER CHECK (quantity > 0)

## Slide 4. ЗАЧЕМ РАЗДЕЛЯТЬ УРОВНИ
(id 56, https://easeweb.host/movie/lesson-2/4/)
- ЗАЧЕМ РАЗДЕЛЯТЬ УРОВНИ
- Независимость данных
- Физическая независимость
  Индекс и способ хранения можно менять без изменения запросов, если сохраняется интерфейс БД. Это цель, а не автоматическая гарантия.
  Логическая независимость
  VIEW может сохранить интерфейс чтения при изменении таблиц. Запись, миграции и совместимость приложения проверяют отдельно.
- Что возможно на практике
- • индекс добавили
  • таблицу разбили на две, отчёты не переписывали
  • сменили сервер БД без изменения интерфейса
  • новое поле не сломало бэк
- Независимость требует стабильного интерфейса и проверки изменений.

## Slide 5. РЕЛЯЦИОННАЯ МОДЕЛЬ
(id 57, https://easeweb.host/movie/lesson-2/5/)
- РЕЛЯЦИОННАЯ МОДЕЛЬ
- Глоссарий
- НА ПРАКТИКЕ
- ПРИМЕР
- В ТЕОРИИ
- Отношение
- Таблица
- players
- Кортеж
- Строка
- один игрок
- Атрибут
- Столбец
- nickname
- VARCHAR(16), только латиница
- Домен
- Тип и ограничения
- Термины из левой колонки встретятся в учебниках и на экзамене. В работе говорят словами из средней.

## Slide 6. КЛЮЧИ
(id 58, https://easeweb.host/movie/lesson-2/6/)
- КЛЮЧИ
- [button "→ зан. 3: ссылки без FK"] -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- Чем строка отличается от всех остальных
- Первичный
- Потенциальный
- PRIMARY KEY
- UNIQUE + NOT NULL
- Однозначно определяет строку, не NULL. Менять можно, но обычно незачем.
- Уникален и минимален: лишних колонок нет. В PostgreSQL UNIQUE по умолчанию допускает несколько NULL; ключу нужен NOT NULL.
- players.player_id
- players.nickname
- Внешний
- Составной
- FOREIGN KEY
- PRIMARY KEY (a, b)
- Ссылка на PK или UNIQUE той же или другой таблицы. База проверяет существование строки, на которую ссылаются.
- Строку определяет не одна колонка, а две или больше.
- match_participants.player_id
- (match_id, player_id)
- Для самостоятельных сущностей обычно удобен стабильный суррогат. Для таблиц связей часто подходит составной ключ из двух внешних.

## Slide 7. СВЯЗИ
(id 59, https://easeweb.host/movie/lesson-2/7/)
- СВЯЗИ
- Три вида связей между сущностями
- [card] 1 : N · Игрок и его покупки · У одного игрока много покупок, у покупки один игрок. Ключ ставится на стороне «многие». -> slide 8 "СВЯЗЬ 1 : N"
- [card] N : M · Игроки и матчи · Игрок был во многих матчах, в матче много игроков. Напрямую не выражается. -> slide 9 "СВЯЗЬ МНОГИЕ-КО-МНОГИМ"
- 1 : 1
- Игрок и его прогресс
- PK = FK у прогресса: не более одной строки на игрока. Наличие строки у каждого игрока не гарантируется.
- Для каждой стороны: сколько связанных строк допустимо и обязательны ли они? Отсутствие связи: NULL в FK или отсутствие дочерней строки.

## Slide 8. СВЯЗЬ 1 : N
(id 60, https://easeweb.host/movie/lesson-2/8/)
- СВЯЗЬ 1 : N
- [button "→ зан. 3: owned и вложение"] -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- Тип предмета и его экземпляры
- item_types
  item_type_id PK
  name 'Клинок зари'
  base_damage 45
  price 100
- item_instances
  instance_id PK
  item_type_id FK -> item_types
  owner_id FK -> players
  durability 73
- одинаково у всех экземпляров
- своё у каждого меча
- Где хранить прочность?
- Нужна ли уникальность (owner_id, item_type_id)?
- У экземпляра. Проверка: если значение меняется у одного предмета, а не у всех сразу — это свойство экземпляра, а не типа.
- Нет, если игрок может иметь два одинаковых меча. Поставите её — запретите это на уровне базы, и баг найдётся через месяц.
- Схема инвентаря выводится из механики игры, а не из шаблона: не стакаются экземпляры, стакается количество.

## Slide 9. СВЯЗЬ МНОГИЕ-КО-МНОГИМ
(id 61, https://easeweb.host/movie/lesson-2/9/)
- СВЯЗЬ МНОГИЕ-КО-МНОГИМ
- N : M всегда превращается в таблицу
- players
- 1 N
- match_participants
- N 1
- matches
- player_id PK
- match_id + player_id PK
- match_id PK
- Что ещё удобно положить в таблицу связи
- match_participants
  match_id FK -> matches
  player_id FK -> players
  team 1 или 2 <- атрибут самой связи
  kills deaths assists damage <- статистика в этом матче
- Таблица связи не «техническая». В неё попадают атрибуты, которые принадлежат не игроку и не матчу, а именно их сочетанию.

## Slide 10. ПОРЯДОК РАБОТЫ
(id 62, https://easeweb.host/movie/lesson-2/10/)
- ПОРЯДОК РАБОТЫ
- От описания игры до DDL
- [card] 2 · Выделить сущности · Существительные из описания: игрок, матч, предмет -> slide 11 "ER-МОДЕЛИРОВАНИЕ"
- [card] 3 · Определить связи · Сколько с каждой стороны: 1:1, 1:N, N:M -> slide 7 "СВЯЗИ"
- 1
- Собрать требования
- Что игра должна помнить между сессиями
- [card] 4 · Атрибуты и ключи · Что храним про каждую сущность и чем различаем строки -> slide 6 "КЛЮЧИ"
- [card] 5 · Нормализовать · Убрать дубли и аномалии, довести до 3НФ -> slide 14 "НОРМАЛЬНЫЕ ФОРМЫ"
- [card] 6 · Записать DDL · Типы, ограничения, индексы под конкретную СУБД -> slide 19 "DBDIAGRAM.IO"

## Slide 11. ER-МОДЕЛИРОВАНИЕ
(id 63, https://easeweb.host/movie/lesson-2/11/)
- ER-МОДЕЛИРОВАНИЕ
- Сущность, атрибут, связь
- «Игрок с уникальным name и lvl участвует в матчах и покупает предметы за голду»
- Сущность
  Объект предметной области, о котором надо хранить данные. В тексте описания это существительное: игрок, матч, предмет.
  Атрибут
  Свойство сущности: ник, уровень, цена. Отвечает на вопрос «что мы про неё знаем».
  Связь
  Отношение между сущностями. В тексте это глагол: игрок покупает предмет, игрок участвует в матче.
- существительные
  сущности → таблицы
  глаголы
  связи → внешние ключи
  признаки
  атрибуты → колонки

## Slide 12. С ЧЕГО НАЧИНАЕТСЯ БЕДА
(id 64, https://easeweb.host/movie/lesson-2/12/)
- С ЧЕГО НАЧИНАЕТСЯ БЕДА
- [button "← зан. 1: покупка"] -> presentation "ITMO DB Lesson 1" (lesson-1)
- Всё в одной таблице
- purchases
  player_nick current_player_level item_name current_item_price item_type bought_at
  Klinok 12 Клинок зари 100 weapon 10:15
  Zarya 7 Клинок зари 100 weapon 10:22
  Klinok 12 Малое лечение 25 potion 11:03
  Klinok 13 Клинок зари 100 weapon 12:40
- Что здесь не так?

## Slide 13. ЦЕНА ДУБЛИРОВАНИЯ
(id 65, https://easeweb.host/movie/lesson-2/13/)
- ЦЕНА ДУБЛИРОВАНИЯ
- Три аномалии
- Аномалия
- Аномалия
- Аномалия
- Вставки
- Обновления
- Удаления
- Новый предмет нельзя завести в базе, пока его никто не купил: строка purchases без покупки не существует.
- Цена меча лежит в трёхстах строках. Меняем — и одну пропускаем. Теперь у базы два мнения о цене.
- Удалили последнюю покупку зелья — вместе с ней исчезло само зелье. Данных о предмете больше нигде нет.
- [card] Все три растут из одного корня: один факт записан в нескольких местах. Нормализация это способ убрать повторы. -> slide 14 "НОРМАЛЬНЫЕ ФОРМЫ"

## Slide 14. НОРМАЛЬНЫЕ ФОРМЫ
(id 66, https://easeweb.host/movie/lesson-2/14/)
- НОРМАЛЬНЫЕ ФОРМЫ
- [button "← зан. 1: slots в документе"] -> presentation "ITMO DB Lesson 1" (lesson-1)
- 1НФ — каждое значение атомарно
- Нарушение
- Приведено к 1НФ
- items
  item_id name slots
  1 Клинок зари "main_hand, off_hand"
  2 Щит зари "off_hand"
- item_slots
  item_id slot
  1 main_hand
  1 off_hand
  2 off_hand
- Почему это важно
- • Нет внешнего ключа на справочник: CHECK по шаблону значение проверит, но связи не создаст
  • Нельзя объявить уникальность пары (предмет, слот) и дубль в списке база пропустит
  • У связи не может быть своих атрибутов: «слот открывается с 10 уровня» хранить негде
  • Добавить слот значит переписать всё значение, а не вставить одну строку

## Slide 15. НОРМАЛЬНЫЕ ФОРМЫ
(id 67, https://easeweb.host/movie/lesson-2/15/)
- НОРМАЛЬНЫЕ ФОРМЫ
- 2НФ — нет зависимости от части ключа
- Нарушение: текущий уровень вне кандидатных ключей и зависит только от player_id
- match_participants
  match_id + player_id (PK) kills deaths current_player_level
  1 42 7 2 12 <- зависит только от player_id
  2 42 4 3 12 <- и потому повторяется
- Приведено к 2НФ: то, что зависит от игрока, в таблицу игроков
- match_participants players
  match_id + player_id PK player_id PK
  kills deaths assists level nickname UNIQUE NOT NULL
  region created_at
  текущий уровень в одном месте; изменить его можно одной операцией
- Проверять надо все кандидатные ключи, а не только выбранный первичный. Добавление суррогата само по себе частичную зависимость не убирает.

## Slide 16. НОРМАЛЬНЫЕ ФОРМЫ
(id 68, https://easeweb.host/movie/lesson-2/16/)
- НОРМАЛЬНЫЕ ФОРМЫ
- [button "→ зан. 3: снимок и копия"] -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- 3НФ — нет зависимости через третью колонку
- Нарушение: цена зависит от предмета, а предмет от покупки.
- purchases
  purchase_id player_id item_id item_name current_item_price
  1 42 sword Клинок зари 100
  2 7 sword Клинок зари 100 <- те же данные снова
  purchase_id -> item_id -> item_name, current_item_price
- Приведено к 3НФ: свойства предмета в таблице предметов
- purchases items
  purchase_id PK item_id PK
  player_id FK -> players name
  item_id FK -> items price
  price_paid bought_at type
  текущую цену меча правим в одном месте; price_paid в старых чеках не меняется
- «The key, the whole key, and nothing but the key, so help me Codd»

## Slide 17. ИТОГ НОРМАЛИЗАЦИИ
(id 69, https://easeweb.host/movie/lesson-2/17/)
- ИТОГ НОРМАЛИЗАЦИИ
- Одна таблица превратилась в четыре
- players
- purchases
- items
- item_slots
- player_id PK
  nickname
  level
  region
- purchase_id PK
  player_id FK
  item_id FK
  price_paid
  bought_at
- item_id PK
  name price
  type
- item_id FK
  slot
  PK (item_id, slot)
- Цена предмета — в одном месте
- Предмет живёт без покупок
- Удаление покупки безопасно
- Меняем один раз, без копий цены в покупках
- Можно завести в каталоге заранее
- Предмет и игрок остаются на месте
- Плата за нормализацию это JOIN трёх таблиц вместо чтения одной строки.

## Slide 18. ОБРАТНЫЙ ХОД
(id 70, https://easeweb.host/movie/lesson-2/18/)
- ОБРАТНЫЙ ХОД
- [button "→ зан. 3: копии в NoSQL"] -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- Когда дублировать правильно
- Не денормализация
- Вычисляемое поле
- Счётчик вместо COUNT
- price_paid в purchases
- duration_sec в matches
- matches_played у игрока
- Сколько заплатили тогда и сколько предмет стоит сейчас это два разных факта. 3НФ не нарушена, дублирования здесь нет вообще.
- Разность времён: GENERATED ... STORED. Вычисление при записи и место на диске. Только текущая строка, IMMUTABLE; допустимость зависит от типов.
- Когда COUNT(*) по миллиону строк вызывается на каждое открытие профиля. Ценой становится обязанность поддерживать счётчик в актуальном состоянии.
- Шесть вопросов, без ответов на которые денормализация не принимается
- 1. Какой запрос дешевеет?
- 2. Где источник истины?
- 3. Кто обновляет копию?
- 4. Допустима ли задержка?
- 5. Как найти расхождение?
- 6. Как его исправить?
- «Меньше JOIN» — не обоснование. Сначала доводим до 3НФ, отступаем только там, где измерили проблему и записали причину.

## Slide 19. DBDIAGRAM.IO
(id 71, https://easeweb.host/movie/lesson-2/19/)
- DBDIAGRAM.IO
- Три вещи, которых не было в практике 1
- Составной первичный ключ
  Блок Indexes с пометкой pk. Пара колонок определяет строку.
  Составная уникальность
  Ввели отдельный id, уникальность пары надо объявить отдельно, иначе игрок попадёт в матч дважды.
  CHECK есть, Note — не CHECK
  check: и checks { } задают CHECK, Note лишь поясняет. Ссылки ref требуют объявленных matches и players.
- // A или B: альтернативы, не вставлять вместе
  Table match_participants {
  match_id bigint [not null, ref: > matches.match_id]
  player_id bigint [not null, ref: > players.player_id]
  team smallint [not null, check: `team in (1,2)`]
  kills int [not null, default: 0, check: `kills >= 0`]
  Indexes {
  (match_id, player_id) [pk]
  }
  Note: 'Один игрок — не более одной записи на матч'
  }
  // B: вместо A (показаны только ключи и ссылки)
  Table match_participants {
  participation_id bigint [pk, increment]
  match_id bigint [not null, ref: > matches.match_id]
  player_id bigint [not null, ref: > players.player_id]
  Indexes {
  (match_id, player_id) [unique]
  }
  }

## Slide 20. ИТОГИ
(id 72, https://easeweb.host/movie/lesson-2/20/)
- ИТОГИ
- Что забрать с занятия
- [card] Сначала сущности, потом таблицы · Существительные из описания игры = сущности, глаголы связи -> slide 11 "ER-МОДЕЛИРОВАНИЕ"
- [card] 3НФ по умолчанию · Каждый атрибут зависит от ключа, всего ключа и ничего кроме ключа -> slide 16 "НОРМАЛЬНЫЕ ФОРМЫ"
- [card] N:M всегда третья таблица · И в неё попадают атрибуты самой связи -> slide 9 "СВЯЗЬ МНОГИЕ-КО-МНОГИМ"
- [card] Далее: занятие 3 → · MongoDB и Redis: те же вопросы о копиях и целостности вне реляционной схемы -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- Практика 2. Правила игры, схема и технологии
- Правила игры; проверка схемы до 3НФ и разбор зависимостей. Протоколы, стек сервисов, БД, кэш и очередь: обосновать выбор и сравнить минимум одну альтернативу.

## Slide 21. ПРОВЕРКА СЕБЯ
(id 73, https://easeweb.host/movie/lesson-2/21/)
- ПРОВЕРКА СЕБЯ
- [button "Далее: занятие 3 →"] -> presentation "ITMO DB lesson 3" (itmo-db-lesson-3)
- Контрольные вопросы
- [card] Чем концептуальная модель отличается от логической и физической? · 1 -> slide 3 "УРОВНИ ПРОЕКТИРОВАНИЯ"
- [card] Почему текущая цена предмета и цена покупки хранятся отдельно? · 5 -> slide 18 "ОБРАТНЫЙ ХОД"
- [card] Почему отдельный id не гарантирует отсутствия дубликатов? · 2 -> slide 19 "DBDIAGRAM.IO"
- [card] Что нужно определить до того, как заводить производный счётчик? · 6 -> slide 18 "ОБРАТНЫЙ ХОД"
- [card] Когда у связи появляются собственные атрибуты? · 3 -> slide 9 "СВЯЗЬ МНОГИЕ-КО-МНОГИМ"
- [card] Чем ссылка на объект другого сервиса отличается от внешнего ключа? · 7 -> slide 6 "КЛЮЧИ"
- [card] Чем нарушение 2НФ отличается от нарушения 3НФ? · 4 -> slide 15 "НОРМАЛЬНЫЕ ФОРМЫ"
- [button "← План занятия"] -> slide 2 "План занятия"
