Когда я изучал раздел базы данных в Fullstack Academy, кто-то спросил, можно ли указать массив в качестве одного из атрибутов. Нравится:

+---------+-------------------------+
| Product |          Color          |
+---------+-------------------------+
| Pen     | ['Red','Yellow','Blue'] |
+---------+-------------------------+

Вместо:

+---------+--------+
| Product | Color  |
+---------+--------+
| Pen     | Red    |
| Pen     | Yellow |
| Pen     | Blue   |
+---------+--------+

Хотя некоторые реляционные базы данных, такие как PostgreSQL, позволяют пользователям использовать массив. Делать это не рекомендуется.

Почему?

Потому что это нарушает правила нормализации базы данных.

Что такое нормализация базы данных?

Нормализация базы данных — это процесс структурирования «реляционной» базы данных, что делает ее более удобной в сопровождении. Существует множество различных форм от 1NF до 6NF.

Большинство считает, что реляционная база данных должна, по крайней мере, удовлетворять требованиям 1NF, 2NF и 3NF, а все, что выходит за рамки 4NF, предназначено в основном для академических интересов. И размещение массива в одном из атрибутов нарушит правило 1NF (ячейки должны иметь только одно значение).

Но почему нас это волнует? PostgreSQL позволяет мне это делать, почему бы мне не использовать его?

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

Таблица 1NF может быть представлена ​​как:

+---------+--------+----------+
| Product | Color  | Quantity |
+---------+--------+----------+
| Pen     | Red    |       10 |
| Pen     | Yellow |       15 |
| Pen     | Blue   |        5 |
+---------+--------+----------+

Если в таблице используются массивы, как мы будем расширять эту таблицу при добавлении атрибутов?

Этот?

+---------+-------------------------+-----------+
| Product |         Color           | Quantity  |
+---------+-------------------------+-----------+
| Pen     | ['Red','Yellow','Blue'] | [10,15,5] |
+---------+-------------------------+-----------+

Или это?

+---------+-----------------------------------+
| Product |         Color & Quantity          |
+---------+-----------------------------------+
| Pen     | ['Red',10, 'Yellow',15, 'Blue',5] |
+---------+-----------------------------------+

Люди, которые используют SQL для извлечения данных, будут ненавидеть вас, если вы создадите такую ​​таблицу.

Почему?

Поскольку они не могут использовать большинство агрегатных функций SQL, таких как MIN(), AVG() и SUM().

Почему?

Потому что эти функции работают только с числовым столбцом, а массив не является числовым. Точно так же вы не можете использовать метод .isNaN() для массива, потому что это метод для числа.

Хорошо. Тогда как нам нормализовать базу данных?

Нормализация базы данных должна быть выполнена путем выполнения сначала нижнего уровня NF, а затем перехода к следующей форме. Вот пример превращения UNF (Unnormalize Form) в 3NF. Я собираюсь использовать аналогичную таблицу, как указано выше.

Таблица исходных данных:

+---------+-------------------+------------------------+----------+
| Product |       Color       |        Supplier        | Location |
+---------+-------------------+------------------------+----------+
| Pen     | Red, Yellow, Blue | Smith's Llc.           | VA       |
| Eraser  | White             | Stationery Supply Inc. | NJ       |
+---------+-------------------+------------------------+----------+

1NF: атомарные столбцы (ячейки имеют одно значение)

Как видите, в столбце Color есть несколько значений. Поэтому мы должны разделить их.

Таблица 1НФ:

+---------+--------+------------------------+----------+
| Product | Color  |        Supplier        | Location |
+---------+--------+------------------------+----------+
| Pen     | Red    | Smith's Llc.           | VA       |
| Pen     | Yellow | Smith's Llc.           | VA       |
| Pen     | Blue   | Smith's Llc.           | VA       |
| Eraser  | White  | Stationery Supply Inc. | NJ       |
+---------+--------+------------------------+----------+

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

Звучит сложно, верно? По сути, это означает, что если столбец не связан напрямую с текущим основным элементом (в данном примере это «product») таблицы, поместите эти столбцы в другую таблицу! Местонахождение поставщика не связано с самим продуктом, поэтому оно должно находиться в другой таблице. 2NF обычно довольно сбивает с толку, иногда мы просто не уверены, должны ли эти столбцы оставаться в одной таблице или помещать их в другую таблицу. Один простой способ сделать это, если вы можете разделить их в другую таблицу, вы всегда должны делать это.

Таблицы 2NF:

+---------+--------+
| Product | Color  |
+---------+--------+
| Pen     | Red    |
| Pen     | Yellow |
| Pen     | Blue   |
| Eraser  | White  |
+---------+--------+
and
+------------------------+-------------------+
|     Supplier Name      | Supplier Location |
+------------------------+-------------------+
| Smith's Llc.           | VA                |
| Stationery Supply Inc. | NJ                |
+------------------------+-------------------+

3NF: каждая нетривиальная функциональная зависимость начинается с суперключа или заканчивается первичным атрибутом (без транзитивных функциональных зависимостей не первичных атрибутов на ключах-кандидатах).

3NF в основном означает, что вам нужно иметь первичный ключ для каждой таблицы в базе данных, и каждый столбец в этой конкретной таблице должен зависеть от него. Одна вещь, которую люди всегда забывают, это зависимость. Например, распространенной ошибкой является наличие столбца итого для таблицы, содержащей цену и количество. Столбец итого не должен быть там, потому что итого зависит от числа в столбце цена и количества столбец (всего = цена * количество). Это не будет показано в примере, но стоит упомянуть.

Таблицы 3NF:

+-----------+---------+--------+
| Product # | Product | Color  |
+-----------+---------+--------+
|       001 | Pen     | Red    |
|       002 | Pen     | Yellow |
|       003 | Pen     | Blue   |
|       004 | Eraser  | White  |
+-----------+---------+--------+
and
+------------+------------------------+-------------------+
| Supplier # |     Supplier Name      | Supplier Location |
+------------+------------------------+-------------------+
|        001 | Smith's Llc.           | VA                |
|        002 | Stationery Supply Inc. | NJ                |
+------------+------------------------+-------------------+

Вуаля! Теперь у нас есть таблицы, удовлетворяющие 3НФ.

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

+----------------+---------+--------+----------------+
| Product # (PK) | Product | Color  | Supplier #(FK) |
+----------------+---------+--------+----------------+
|            001 | Pen     | Red    |            001 |
|            002 | Pen     | Yellow |            001 |
|            003 | Pen     | Blue   |            001 |
|            004 | Eraser  | White  |            002 |
+----------------+---------+--------+----------------+

Затем нам просто нужно использовать SQL, чтобы указать базе данных объединить две таблицы на основе внешнего ключа в последнем столбце таблицы Product.

В заключение: НЕ используйте массив в реляционной базе данных. 😊