Към съдържанието
wannacode

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

Накратко: Нормализацията разделя данните в таблици така, че всеки факт да е записан на едно място, за да няма аномалии при промяна, добавяне и изтриване.

Проблем

Excel-ът дойде в базата

Помниш таблицата с поръчките от първия урок на курса? Някой я е пренесъл в базата както е: един ред за всеки продукт в поръчка, а до него датата, клиентът, имейлът му и градът.

flat_orders

  • order_idinteger *
  • ordered_attext *
  • customertext *
  • emailtext *
  • citytext
  • producttext *
  • pricereal *
  • quantityinteger *
SQLпразна база

11 реда, а клиентите са трима. Името и имейлът на Мария са записани 6 пъти. Погледни и града ѝ в поръчка 6.

Идеята

Аномалии

Когато един факт е записан на много места, всяка промяна е риск. Тези проблеми се казват аномалии. Те са три вида.

При промяна. Мария сменя имейла си. Колегата го поправя само в последната ѝ поръчка:

SQLпразна база

Сега базата казва две неща наведнъж. Кой имейл е верният?

Виж го

Добавяне и изтриване

При добавяне. Магазинът започва да продава овесени ядки. Още никой не ги е поръчал. Къде да запишеш цената им?

SQLпразна база

Ред без поръчка не може да влезе. Продуктът съществува само докато някой го купи.

При изтриване. Елена отказва поръчка 4 и я трият. Това е единствената поръчка с тигана:

SQLпразна база

Заедно с поръчката изчезна и това, че магазинът продава тиган за 24,90 €.

Идеята

Нормални форми

Подреждането на таблиците така, че всеки факт да е на едно място, се казва нормализация. Правилата са на стъпки, нормални форми. Всяка следваща включва предишната.

Първа (1НФ): една стойност в клетка. Без списъци като „веган, био“ и без колони tag1, tag2, tag3.

SQLпразна база

Само белият хляб, макар че веган са и пълнозърнестият хляб, ябълките и кафето. Пълнозърнестият е веган, но клетката му е „веган, био“. С LIKE '%веган%' ще го намериш, но не можеш да преброиш продуктите по етикет, да поправиш „био“ навсякъде или да не пуснеш правописна грешка. Етикетите отиват в таблица за връзка, както в първия урок на модула.

Втора (2НФ): всяка колона зависи от целия ключ. Има значение, когато ключът е от няколко колони:

order_lines

  • PKorder_idinteger
  • PKproduct_idinteger
  • product_nametext *
  • quantityinteger *

Името на продукта зависи само от product_id, не от поръчката. Ще се повтаря във всяка поръчка с този продукт. Мястото му е в products.

Трета (3НФ): колоните зависят само от ключа, а не от друга колона:

products

  • PKidinteger
  • nametext *
  • category_idinteger *
  • category_nametext *
  • pricereal *

category_name се определя от category_id, не от продукта. Ако категорията се преименува, трябва да поправиш всички продукти в нея. Мястото на името е в categories.

По-точно, за любопитните

Кратката формула на трите форми е: всяка колона зависи „от ключа, от целия ключ и от нищо друго освен ключа“.

Има и по-високи форми (на Бойс и Код, четвърта, пета), но на практика таблица в трета форма обикновено вече няма тези аномалии.

flat_orders нарушава втората форма. Ключът на реда е поръчката и продуктът, а датата и клиентът зависят само от поръчката. Нарушава и третата: градът и имейлът зависят от клиента, не от поръчката.

Пробвай

Коя форма?

1. В order_lines ключът е (order_id, product_id), а product_name зависи само от product_id. Коя форма е нарушена?
2. В products има category_id и category_name. Коя форма е нарушена?

Идеята

Разделяне

Разделяш flat_orders на таблиците на магазина: customers, products, orders и order_items. Започваш с тези, които не сочат към никого.

INSERT може да вземе редовете от SELECT вместо от VALUES. С DISTINCT всеки продукт влиза веднъж:

products

  • PKidinteger
  • nametext *
  • pricereal *
SQLпразна база

От 11 реда остават 6 продукта, всеки с id. Цената остава и в редовете на поръчките, защото това е цената, на която е купено. Защо това не е повторение, ще видим в следващия урок.

Виж го

Excel-ът е мръсен

Същото с клиентите:

customers

  • PKidinteger
  • nametext *
  • emailtext *
  • citytext
SQLпразна база

Грешка. В поръчка 6 градът на Мария е „Софя“. DISTINCT сравнява целия ред, затова вижда двама клиенти с един имейл, а UNIQUE не ги пуска. Това е добре: правилото хвана грешката, вместо да я пренесе в новата база.

Първо поправяш данните, после разделяш:

SQLпразна база

Почти всеки пренос от Excel започва с такова почистване.

Пробвай

Поръчките

orders

  • PKidinteger
  • FKcustomer_idinteger *→ customers
  • ordered_attext *

Продуктите и клиентите вече са разделени. Напълни orders (id, customer_id, ordered_at) от flat_orders. Номерът на поръчката е order_id, а клиентът се намира по имейла.

SQLпразна база

Упражнение

Две задачи

1. Остава таблицата за връзка между поръчките и продуктите.

order_items

  • PKorder_idinteger→ orders
  • PKproduct_idinteger→ products
  • quantityinteger *
  • unit_pricereal *

Напълни order_items от flat_orders: номера на поръчката, id-то на продукта, броя и цената.

SQLпразна база

2. Сега отчетът от първия урок на курса отнема една заявка. Колко е похарчил всеки клиент?

Покажи името на всеки клиент и сбора на покупките му (броя по цената):

SQLпразна база

Тест

Провери се

1. Мария смени имейла си, а в таблицата той е на 40 реда. Колегата поправи 38. Как се казва това?
2. Кое нарушава първата нормална форма?
3. Какво прави INSERT INTO products (name, price) SELECT DISTINCT product, price FROM flat_orders?
4. Защо разделянето на Мариините поръчки спря с грешка UNIQUE?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·