Пекарната, етап 2: данните
Накратко: Вторият етап пълни таблиците на пекарната с INSERT, RETURNING и INSERT … SELECT, а първата поръчка се записва в транзакция.
Проблем
Празни таблици
Таблиците от етап 1 са готови и празни. Пекарната има три категории, осем продукта, пет клиенти и първите си поръчки. В този етап ги записваш, и всяко правило от етап 1 ще пази данните.
Всички полета тръгват от базата, която си направил досега: петте таблици и данните от предишните стъпки на етапа.
Идеята
Първият ред
INSERT INTO таблица (колони) VALUES (стойности) записва ред. Колоната id не е в списъка: INTEGER PRIMARY KEY без стойност получава следващото число сам.
Колоните в скобите са важни. INSERT INTO categories VALUES (1, 'Хляб') също работи, но зависи от реда на колоните в таблицата и се чупи при първата нова колона.
Пробвай
Трите категории
Запиши трите категории с един INSERT: Хляб, Сладкиши и Напитки, в този ред (хлябът да е 1, сладкишите 2, напитките 3).
Решение
INSERT INTO categories (name) VALUES
('Хляб'),
('Сладкиши'),
('Напитки');Няколко реда в VALUES, разделени със запетая: ('Хляб'), ('Сладкиши'), ('Напитки'). Редът на записване дава номерата. С напитките преди сладкишите напитките стават 2, а продуктите после сочат към грешната категория.
Идеята
Какво записа базата
Програмата често трябва да знае номера на новия ред, например за да покаже „Продуктът е добавен с номер 1“. RETURNING връща колоните на записания ред:
RETURNING връща и стойностите, които базата е сложила сама: без stock в списъка щеше да върне 0 от DEFAULT. Осемте продукта се записват с един INSERT:
По-точно, за любопитните
RETURNING има в SQLite от версия 3.35 и в PostgreSQL. В MySQL го няма: там номерът се взима с LAST_INSERT_ID(), а в SQLite и със last_insert_rowid().
Виж го
Правилата пазят
Правилата от етап 1 вече работят. Продукт с цена 0:
Продукт в категория 9, която я няма:
Продукт без име:
Нито един от трите не влиза. Грешката идва от базата, затова е все едно кой я праща: сайтът, скрипт за внос от Excel или колега в конзолата.
Пробвай
Клиентите
Запиши петте клиента с един INSERT (колони name, email, city): Иван Колев, ivan.kolev@example.com, София; Ана Петрова, ana.petrova@example.com, Пловдив; Борис Николов, boris.nikolov@example.com, София; Десислава Маринова, desislava.marinova@example.com, Варна; Калин Тодоров, kalin.todorov@example.com, без град.
Решение
INSERT INTO customers (name, email, city) VALUES
('Иван Колев', 'ivan.kolev@example.com', 'София'),
('Ана Петрова', 'ana.petrova@example.com', 'Пловдив'),
('Борис Николов', 'boris.nikolov@example.com', 'София'),
('Десислава Маринова', 'desislava.marinova@example.com', 'Варна'),
('Калин Тодоров', 'kalin.todorov@example.com', NULL);Градът на Калин е NULL, без кавички. '' е празен текст: град, който е известен и е празен, а не „не знаем“. WHERE city IS NULL после няма да намери Калин. Имейлите се пишат точно така, с малки букви.
По-точно, за любопитните
UNIQUE сравнява текста буква по буква: Ivan.Kolev@example.com и ivan.kolev@example.com са два различни имейла за базата. Затова програмата прави имейла с малки букви, преди да го запише, или таблицата пази UNIQUE върху lower(email) с индекс (CREATE UNIQUE INDEX … ON customers (lower(email))).
Виж го
Същият имейл
Иван от Пловдив е друг човек със същия имейл, а базата не го пуска. Регистрацията в сайта показва точно тази грешка като „Този имейл вече е регистриран“.
Идеята
Поръчката и нейните редове
Поръчка без status получава 'new' от DEFAULT:
Истинската поръчка обаче са два вида редове: един в orders и няколко в order_items. Ако програмата падне между тях, остава поръчка без нито един продукт. Затова двете се пишат в транзакция: BEGIN, вмъкванията, COMMIT. До COMMIT нищо не е окончателно.
Пробвай
Първата поръчка
Иван (клиент 1) поръча на 2026-10-01 два селски хляба (продукт 1 по 2.40) и три кроасана (продукт 4 по 1.30). Поръчката е доставена. Запиши я в една транзакция: поръчката (тя ще е номер 1) и двата ѝ реда.
Решение
BEGIN;
INSERT INTO orders (customer_id, ordered_at, status) VALUES (1, '2026-10-01', 'delivered');
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 2, 2.40),
(1, 4, 3, 1.30);
COMMIT;BEGIN;, INSERT INTO orders (customer_id, ordered_at, status) VALUES (1, '2026-10-01', 'delivered');, после INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (1, 1, 2, 2.40), (1, 4, 3, 1.30); и COMMIT;. Ред с поръчка 2 спира от външния ключ: такава поръчка още няма. Кроасанът по 2.40 е цената на хляба, не неговата.
Виж го
Отказ
Програмата е записала поръчката и разбира, че нещо не е наред (картата е отхвърлена, редът е спрял с грешка). Тогава вместо COMMIT праща ROLLBACK:
По време на транзакцията поръчките са 2, а след ROLLBACK пак 1: втората е изчезнала, все едно не е имало INSERT.
По-точно, за любопитните
Тук всяко поле спира на първата грешка, като конзолата. Програмата прави същото с try: при изключение праща ROLLBACK. Транзакцията не се отменя сама при грешка в една заявка, затова ROLLBACK е работа на програмата.
Виж го
Останалите поръчки
Поръчките от 2 до 6 се записват по същия начин. Ето ги, с броя поръчки по статус накрая:
Поръчка 4 е отказана, 5 е платена и чака доставка, а 6 е нова. Калин (клиент 5) още не е поръчвал.
Упражнение
Цената от продукта
Сайтът не знае цените наизуст. Когато клиентът купи, цената се взима от products в този момент и се записва в реда. INSERT … SELECT записва редовете, които връща един SELECT.
Калин (клиент 5) поръчва днес две багети (продукт 3). Запиши поръчката (тя ще е номер 7, датата е date('now')) и реда ѝ, като unit_price се взима от products с INSERT … SELECT.
Решение
INSERT INTO orders (customer_id, ordered_at) VALUES (5, date('now'));
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
SELECT 7, id, 2, price FROM products WHERE id = 3;INSERT INTO order_items (order_id, product_id, quantity, unit_price) SELECT 7, id, 2, price FROM products WHERE id = 3;. SELECT връща един ред: 7, 3, 2 и цената на багетата. С 1.80 на ръка цената е грешна (това е кафето), а с WHERE id = 2 редът е за ръжения хляб.
Идеята
Пълната база
Шестте поръчки са в базата. Колко реда има всяка таблица:
Следващият етап задава въпроси към тези данни: менюто, сметката на всяка поръчка, кой какво купува.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Ограничения: правила за данните · Естествен ключ и денормализация · ACID: всичко или нищо · BEGIN, COMMIT, ROLLBACK
- Задачи по темата: Проект: пекарната от нула