Пекарната, етап 5: базата работи сама
Накратко: Петият етап добавя индекс, изглед със сумите на поръчките, тригер, който сваля наличността, и нова колона с ALTER TABLE.
Проблем
Нещата, за които се сещаш
В етап 4 наличността се сваляше от заявка, написана на ръка, а сумата на поръчка се смяташе с три таблици всеки път. Пекарната расте: поръчките стават хиляди, а хората, които пишат заявки, стават повече. Нещата, които зависят от това някой да се сети, рано или късно се забравят.
Този етап прехвърля работата към базата: индекс за бързо търсене, изглед за сумите, тригер за склада и нова колона за телефона.
Идеята
Как базата търси
Страницата „Моите поръчки“ пита за поръчките на един клиент. EXPLAIN QUERY PLAN показва как базата ще го направи:
SCAN orders: базата чете всяка поръчка и гледа клиента. При шест поръчки няма значение, при милион е бавно.
Пробвай
Индексът
Направи индекс orders_customer върху колоната customer_id на orders.
Решение
CREATE INDEX orders_customer ON orders (customer_id);CREATE INDEX orders_customer ON orders (customer_id);. Проверката гледа коя колона е в индекса. Индекс върху status не помага за търсене по клиент, а индекс върху customers (id) е излишен: първичният ключ вече е индекс.
Същата заявка с индекса:
SEARCH … USING INDEX: базата отива направо при поръчките на клиент 2.
По-точно, за любопитните
SQLite не прави сам индекс за външен ключ, PostgreSQL също. Колона, която сочи към друга таблица, почти винаги иска индекс: по нея се свързва и се търси. Индексът обаче не е безплатен: всеки INSERT и UPDATE го обновява.
Пробвай
Изгледът
Сумата на поръчка е три таблици и GROUP BY всеки път. Изглед (view) е заявка с име: от него се чете като от таблица.
Направи изглед order_totals с номера на поръчката (id), клиента (customer_id), статуса и сумата (total, закръглена до 2 знака) на всяка поръчка.
Решение
CREATE VIEW order_totals AS
SELECT o.id, o.customer_id, o.status,
ROUND(SUM(i.quantity * i.unit_price), 2) AS total
FROM orders o
JOIN order_items i ON i.order_id = o.id
GROUP BY o.id;CREATE VIEW order_totals AS SELECT o.id, o.customer_id, o.status, ROUND(SUM(i.quantity * i.unit_price), 2) AS total FROM orders o JOIN order_items i ON i.order_id = o.id GROUP BY o.id;. Без GROUP BY излиза един ред със сумата на всичко. С SUM(i.unit_price) бройките не се броят.
Виж го
Изгледът като таблица
Колко е похарчил всеки клиент, без отказаните поръчки. Сметката с трите таблици е скрита в изгледа:
Изгледът не пази данни, а заявката си. Нова поръчка се вижда в него веднага:
Пробвай
Тригерът
Тригер е код, който базата пуска сама при INSERT, UPDATE или DELETE в таблица. В тялото му new е новият ред.
Направи тригер take_stock: след всеки нов ред в order_items наличността на продукта от реда намалява с бройките в него.
Решение
CREATE TRIGGER take_stock
AFTER INSERT ON order_items
BEGIN
UPDATE products SET stock = stock - new.quantity
WHERE id = new.product_id;
END;CREATE TRIGGER take_stock AFTER INSERT ON order_items BEGIN UPDATE products SET stock = stock - new.quantity WHERE id = new.product_id; END;. Проверката записва поръчка с две багети и гледа склада: багетите трябва да са 23, а кроасаните да останат 40. Без WHERE намаляват всички продукти. Със stock - 1 двете багети свалят една.
Виж го
Тригерът и правилата
С тригера Калин поръчва 20 козунака, а те са 10:
UPDATE в тригера нарушава CHECK (stock >= 0). Тригерът е част от INSERT, затова и редът на поръчката не влиза: в базата няма ред, който да казва „продадени 20 козунака“, когато те ги няма.
По-точно, за любопитните
Тригерите пазят правило на едно място, но са скрити: който чете кода на програмата, не ги вижда. Затова се ползват пестеливо, за неща, които трябва да станат винаги, каквото и да пише в базата. Наличността тук е такова нещо, а пращането на имейл не е.
Пробвай
Нова колона
Пекарната иска да звъни на клиентите, когато поръчката е готова. Таблицата има данни, затова не се прави наново: ALTER TABLE добавя колона към нея.
Добави на customers колона phone от тип TEXT, която може да липсва.
Решение
ALTER TABLE customers ADD COLUMN phone TEXT;ALTER TABLE customers ADD COLUMN phone TEXT;. Петте клиента получават NULL: телефоните им още не са известни. Телефонът е текст, не число: +359 88… има знак и интервали, а водещата нула на 0888… изчезва в INTEGER.
С NOT NULL не става:
По-точно, за любопитните
ALTER TABLE в SQLite може да добавя, преименува и (от 3.35) маха колони. Ограничение като CHECK или външен ключ към съществуваща колона не може да добави: тогава таблицата се прави наново (нова таблица, INSERT … SELECT, DROP на старата и преименуване). В PostgreSQL ALTER TABLE … ADD CONSTRAINT го прави директно.
Идеята
Какво следва
Базата е пълна, бърза и пази правилата си сама. Последният етап е краят: отчет, копие на базата и DROP на всичко в правилния ред.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Изгледи · Тригери · Индекси · EXPLAIN: планът на заявката