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

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

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

Рассмотрим несколько базовых принципов нормализации реляционных баз данных.

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

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

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

sqlite> CREATE TABLE dates (
   ...> _id INTEGER PRIMARY KEY AUTOINCREMENT,
   ...> page_id INTEGER NOT NULL,
   ...> date TEXT,
   ...> FOREIGN KEY (page_id) 
   ...> REFERENCES pages(_id)
   ...> );

Вывод данных о правках в более информативном виде:

sqlite> SELECT dates.date, pages.title
   ...> FROM dates, pages
   ...> ON dates.page_id == pages._id
   ...> ORDER BY date(dates.date) DESC;
2019-06-04|Boolean Lows
2019-06-03|Amount of Information
2019-06-01|What is Algorithm
2019-05-30|Boolean Lows
2019-05-26|Amount of Information
2019-05-26|Binary System
2019-05-25|What is Information

В SQLite нет типов данных под даты и время. Однако функция date() позволяет преобразовывать текст в дату и сравнивать даты между собой.

Запрос о последних правках каждой страницы:

sqlite> SELECT max(date(dates.date)), 
   ...> pages.url
   ...> FROM dates, pages
   ...> ON dates.page_id == pages._id
   ...> GROUP BY pages.url
   ...> ORDER BY date(dates.date) DESC;
2019-06-04|boolean
2019-06-03|amount-information
2019-06-01|algorithm
2019-05-26|binary
2019-05-25|information

Здесь в каждой группе находится максимальное значение в поле даты. Сортировка выполняется уже после.

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

sqlite> SELECT date FROM dates 
   ...> WHERE page_id == 8 
   ...> ORDER BY date DESC;

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

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

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

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

Введение в реляционные базы данных. SQLite