Перейти к содержанию
Educora
Университет25 мин56 / 59

Теория баз данных

Изучи реляционную модель, ключи, функциональные зависимости, нормализацию 1НФ–3НФ на пошаговом примере, транзакции и ACID, индексы и различия SQL и NoSQL.

Проверь себя
В этом уроке ты узнаешь
  • Различать суперключ, потенциальный, первичный и внешний ключи
  • Нормализовать таблицу до 3НФ по функциональным зависимостям
  • Объяснять свойства ACID и вычислять выигрыш от индекса

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

Реляционная модель и ключи

В реляционной модели, предложенной Эдгаром Коддом в 1970 году, данные хранятся в отношениях (таблицах). Отношение — это множество кортежей (строк) с одинаковыми атрибутами; у каждого атрибута есть домен (множество допустимых значений). Раз это множество, у строк нет порядка и нет дубликатов. Запросы выражаются в реляционной алгебре, а SQL — её практический язык.

π[first_name](σ[city = 'Bakı'](students)) ≡ SELECT first_name FROM students WHERE city = 'Bakı'
где:
  • σвыборка (selection): строки, удовлетворяющие условию, — WHERE
  • πпроекция: нужные столбцы — список SELECT
  • ⋈соединение (join): объединение двух отношений по общему атрибуту — JOIN
КлючОпределениеПример (students)
Суперключлюбой набор атрибутов, однозначно определяющий строку{id}, {id, city}
Потенциальный ключминимальный суперключ (ни один атрибут нельзя убрать){id}
Первичный ключвыбранный потенциальный ключ: уникален и не NULLid
Внешний ключссылка на первичный ключ другой таблицы (ссылочная целостность)enrollments.student_id → students.id
SQL
SELECT COUNT(*)              AS students,
       COUNT(email)          AS with_email,
       COUNT(DISTINCT email) AS distinct_emails,
       COUNT(DISTINCT city)  AS distinct_cities
FROM students;
▸ Ожидаемый результат
students | with_email | distinct_emails | distinct_cities
12 | 10 | 10 | 7
Проверяем кандидатов в ключи на данных: city ключом быть не может (7 разных городов на 12 строк). email уникален там, где заполнен (10 = 10), но у 2 студентов равен NULL — поэтому первичным ключом он быть не может. Заметь: текущие данные никогда не доказывают ключ — ключ задаётся бизнес-правилом.

Нормализация: 1НФ, 2НФ, 3НФ

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

Определение
Функциональная зависимость X → Y

Любые две строки с одинаковыми значениями атрибутов X обязательно совпадают и по атрибутам Y. Например, customer_id → customer_city: зная клиента, знаешь и его город. Нормализация опирается именно на такие зависимости.

ФормаТребованиеЧто устраняет
1NFв каждой ячейке одно атомарное значение, нет повторяющихся группячейки-списки вроде «Laptop, Headphones»
2NF1НФ + ни один неключевой атрибут не зависит от части составного ключачастичные зависимости
3NF2НФ + неключевые атрибуты зависят только от ключа, а не друг от другатранзитивные зависимости
Запоминающаяся формулировка 3НФ: каждый неключевой атрибут должен зависеть «от ключа, от всего ключа и ни от чего, кроме ключа».
Пример 1: приводим таблицу заказов к 3НФ

Таблица магазина: order_id, order_date, customer_id, customer_name, customer_city, products, где в ячейке products список вида «Laptop ×1, Headphones ×2» вместе с ценами и категориями. Приведи её к 1НФ, 2НФ и 3НФ.

Показать решение
1НФ: раскрываем список — одна строка на пару (заказ, товар):
(order_id, product_id, order_date, customer_id, customer_name, customer_city, product_name, category, price, quantity), ключ (order_id, product_id).
Зависимости: order_id → order_date, customer_id; customer_id → customer_name, customer_city; product_id → product_name, category, price; (order_id, product_id) → quantity.
2НФ: выносим то, что зависит от части ключа:
order_items(order_id, product_id, quantity)
orders(order_id, order_date, customer_id, customer_name, customer_city)
products(product_id, product_name, category, price)
3НФ: в orders order_id → customer_id → customer_city — транзитивная зависимость, выносим:
orders(order_id, order_date, customer_id) + customers(customer_id, customer_name, customer_city).
Итог: 4 таблицы. Теперь город Анара меняется в одной ячейке.
SQL
SELECT s.first_name, s.city, c.title, c.teacher, e.score
FROM enrollments AS e
JOIN students AS s ON s.id = e.student_id
JOIN courses AS c ON c.id = e.course_id
WHERE c.teacher = 'Ramin Səfərov'
ORDER BY c.title, s.first_name;
▸ Ожидаемый результат
first_name | city | title | teacher | score
Aysel | Bakı | Algebra | Ramin Səfərov | 92
Fidan | Naxçıvan | Algebra | Ramin Səfərov | 97
Murad | Gəncə | Algebra | Ramin Səfərov | 75
Nigar | Şəki | Algebra | Ramin Səfərov | 85
Leyla | Bakı | Geometry | Ramin Səfərov | 95
Orxan | Sumqayıt | Geometry | Ramin Səfərov | 70
Səbinə | Quba | Geometry | Ramin Səfərov | 79
Учебная база нормализована: students, courses, enrollments. JOIN восстанавливает «плоскую» таблицу только тогда, когда она нужна, — здесь имя преподавателя повторяется 7 раз. Если бы её так и хранили, смена преподавателя потребовала бы обновить 7 строк (аномалия обновления).

Транзакции и ACID

СвойствоСмыслКак обеспечивается
Атомарность (A)всё или ничегоROLLBACK, журнал отката
Согласованность (C)база переходит из одного корректного состояния в другоеограничения: ключи, CHECK, внешние ключи
Изолированность (I)параллельные транзакции не видят незавершённую работу друг другаблокировки, многоверсионность (MVCC)
Долговечность (D)после COMMIT данные переживают даже отключение питанияжурнал упреждающей записи (WAL)
SQL
BEGIN;
UPDATE products SET stock = stock - 2 WHERE name = 'Laptop';
INSERT INTO orders (customer_id, product_id, quantity, order_date)
VALUES (2, 1, 2, '2025-08-01');
ROLLBACK;

SELECT name, stock,
       (SELECT COUNT(*) FROM orders) AS orders_total
FROM products
WHERE name = 'Laptop';
▸ Ожидаемый результат
name | stock | orders_total
Laptop | 8 | 12
Атомарность на практике: транзакция списала со склада 2 ноутбука и записала заказ, а затем была отменена (ROLLBACK). Оба изменения исчезли: остаток снова 8, заказов по-прежнему 12. С COMMIT оба сохранились бы вместе.

Полная изоляция дорога, поэтому SQL предлагает четыре уровня изоляции. READ UNCOMMITTED допускает «грязное чтение» (видны чужие неподтверждённые изменения), READ COMMITTED — «неповторяющееся чтение» (одна и та же строка различается при двух чтениях), REPEATABLE READ — «фантомы» (появление новых строк), а SERIALIZABLE гарантирует тот же результат, что и последовательное выполнение транзакций.

Индексы, SQL и NoSQL

h = ⌈log N / log f⌉h = ⌈log N / log f⌉
где:
  • hвысота индекса B-дерева (число читаемых страниц при поиске)
  • Nчисло строк в таблице
  • fветвление: сколько ключей помещается на странице
Пример 2: выигрыш от индекса

В таблице N = 10⁷ строк, на одну страницу диска помещается 100 строк. Сколько страниц прочитает запрос WHERE email = … без индекса и с индексом B-дерева с ветвлением f = 100?

Показать решение
Без индекса — полный просмотр: 10⁷ / 100 = 10⁵ страниц.
С индексом: h = ⌈log 10⁷ / log 100⌉ = ⌈7/2⌉ = ⌈3,5⌉ = 4 уровня + 1 страница самой строки = ≈ 5 страниц.
Выигрыш ≈ 20 000 раз: O(log N) вместо O(N). Цена: индекс занимает место, и каждый INSERT/UPDATE должен его обновлять.
КритерийSQL (реляционные)NoSQL
модельтаблицы и связидокументы, ключ–значение, семейства столбцов, графы
схемастрогая, задаётся заранеегибкая
транзакцииполный ACIDчасто ограничены, «согласованность в конечном счёте»
масштабированиев основном вертикальное (мощнее сервер)горизонтальное (много серверов)
примерыPostgreSQL, MySQL, SQLite, SQL ServerMongoDB, Redis, Cassandra, Neo4j
Выбор зависит от задачи: банковские счета — SQL, кэш сессий — Redis, социальные связи — графовая база. Многие проекты используют и то, и другое.

Главное

  • Отношение — множество кортежей; потенциальный ключ — минимальный суперключ, первичный — уникален и NOT NULL, внешний защищает целостность.
  • 1НФ — атомарные значения; 2НФ — нет частичных зависимостей; 3НФ — нет транзитивных зависимостей.
  • Нормализация устраняет аномалии обновления, вставки и удаления.
  • ACID: атомарность, согласованность, изолированность, долговечность; ROLLBACK отменяет транзакцию целиком.
  • Индекс B-дерева сокращает поиск с O(N) до O(log N), но замедляет запись.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
grades(student_id, course_id, student_name, score), ключ (student_id, course_id), student_id → student_name. Какую нормальную форму нарушает таблица?