Die Bäckerei, Etappe 2: die Daten
Kurz gesagt: Etappe 2 füllt die Tabellen der Bäckerei mit INSERT, RETURNING und INSERT … SELECT und schreibt die erste Bestellung in einer Transaktion.
Problem
Leere Tabellen
Die Tabellen aus Etappe 1 sind fertig und leer. Die Bäckerei hat drei Kategorien, acht Produkte, fünf Kunden und ihre ersten Bestellungen. In dieser Etappe schreibst du sie hinein, und jede Regel aus Etappe 1 bewacht die Daten.
Alle Felder beginnen mit der Datenbank, die du bisher gebaut hast: die fünf Tabellen und die Daten aus den früheren Schritten der Etappe.
Die Idee
Die erste Zeile
INSERT INTO Tabelle (Spalten) VALUES (Werte) schreibt eine Zeile. Die Spalte id steht nicht in der Liste: INTEGER PRIMARY KEY ohne Wert bekommt von selbst die nächste Nummer.
Die Spalten in Klammern sind wichtig. INSERT INTO categories VALUES (1, 'Brot') funktioniert auch, hängt aber von der Reihenfolge der Spalten in der Tabelle ab und bricht mit der ersten neuen Spalte.
Probier es aus
Die drei Kategorien
Schreibe die drei Kategorien mit einem INSERT: Brot, Teilchen und Getränke, in dieser Reihenfolge (Brot ist 1, Teilchen 2, Getränke 3).
Lösung
INSERT INTO categories (name) VALUES
('Brot'),
('Teilchen'),
('Getränke');Mehrere Zeilen in VALUES, durch Kommas getrennt: ('Brot'), ('Teilchen'), ('Getränke'). Die Reihenfolge des Schreibens vergibt die Nummern. Stehen die Getränke vor den Teilchen, werden die Getränke 2, und die Produkte zeigen später auf die falsche Kategorie.
Die Idee
Was die Datenbank geschrieben hat
Das Programm braucht oft die Nummer der neuen Zeile, zum Beispiel um „Das Produkt wurde mit der Nummer 1 hinzugefügt“ anzuzeigen. RETURNING gibt die Spalten der geschriebenen Zeile zurück:
RETURNING gibt auch die Werte zurück, die die Datenbank selbst eingesetzt hat: Stünde stock nicht in der Liste, käme 0 aus DEFAULT. Die acht Produkte werden mit einem INSERT geschrieben:
Genauer, für Neugierige
RETURNING gibt es in SQLite seit Version 3.35 und in PostgreSQL. MySQL hat es nicht: Dort holt man die Nummer mit LAST_INSERT_ID(), und in SQLite geht es auch mit last_insert_rowid().
Sieh es dir an
Die Regeln wachen
Die Regeln aus Etappe 1 arbeiten jetzt. Ein Produkt mit Preis 0:
Ein Produkt in Kategorie 9, die es nicht gibt:
Ein Produkt ohne Namen:
Keines der drei kommt hinein. Der Fehler kommt von der Datenbank, also spielt es keine Rolle, wer die Zeile schickt: die Seite, ein Importskript aus Excel oder ein Kollege in der Konsole.
Probier es aus
Die Kunden
Schreibe die fünf Kunden mit einem INSERT (Spalten name, email, city): Ivan Kolev, ivan.kolev@example.com, Sofia; Ana Petrova, ana.petrova@example.com, Plovdiv; Boris Nikolov, boris.nikolov@example.com, Sofia; Desislava Marinova, desislava.marinova@example.com, Varna; Kalin Todorov, kalin.todorov@example.com, ohne Stadt.
Lösung
INSERT INTO customers (name, email, city) VALUES
('Ivan Kolev', 'ivan.kolev@example.com', 'Sofia'),
('Ana Petrova', 'ana.petrova@example.com', 'Plovdiv'),
('Boris Nikolov', 'boris.nikolov@example.com', 'Sofia'),
('Desislava Marinova', 'desislava.marinova@example.com', 'Varna'),
('Kalin Todorov', 'kalin.todorov@example.com', NULL);Kalins Stadt ist NULL, ohne Anführungszeichen. '' ist leerer Text: eine Stadt, die bekannt und leer ist, nicht „wir wissen es nicht“. WHERE city IS NULL würde Kalin dann nicht finden. Die E-Mails werden genau so geschrieben, in Kleinbuchstaben.
Genauer, für Neugierige
UNIQUE vergleicht den Text Buchstabe für Buchstabe: Ivan.Kolev@example.com und ivan.kolev@example.com sind für die Datenbank zwei verschiedene E-Mails. Darum macht das Programm die E-Mail vor dem Schreiben zu Kleinbuchstaben, oder die Tabelle hält UNIQUE auf lower(email) mit einem Index (CREATE UNIQUE INDEX … ON customers (lower(email))).
Sieh es dir an
Dieselbe E-Mail
Ivan aus Plovdiv ist eine andere Person mit derselben E-Mail, und die Datenbank lässt ihn nicht hinein. Die Anmeldung auf der Seite zeigt genau diesen Fehler als „Diese E-Mail ist schon registriert“.
Die Idee
Die Bestellung und ihre Zeilen
Eine Bestellung ohne status bekommt 'new' aus DEFAULT:
Eine echte Bestellung besteht aber aus zwei Arten von Zeilen: einer in orders und mehreren in order_items. Stürzt das Programm dazwischen ab, bleibt eine Bestellung ohne ein einziges Produkt. Darum werden die beiden in einer Transaktion geschrieben: BEGIN, die Inserts, COMMIT. Bis zum COMMIT ist nichts endgültig.
Probier es aus
Die erste Bestellung
Ivan (Kunde 1) hat am 2026-10-01 zwei Landbrote (Produkt 1 zu 2,40) und drei Croissants (Produkt 4 zu 1,30) bestellt. Die Bestellung ist geliefert. Schreibe sie in einer Transaktion: die Bestellung (sie bekommt die Nummer 1) und ihre zwei Zeilen.
Lösung
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');, dann INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (1, 1, 2, 2.40), (1, 4, 3, 1.30); und COMMIT;. Eine Zeile mit Bestellung 2 weist der Fremdschlüssel ab: Es gibt diese Bestellung noch nicht. Das Croissant zu 2,40 ist der Preis des Brotes, nicht sein eigener.
Sieh es dir an
Abbrechen
Das Programm hat die Bestellung geschrieben und merkt, dass etwas nicht stimmt (die Karte wurde abgelehnt, eine Zeile brach mit einem Fehler ab). Dann schickt es statt COMMIT ein ROLLBACK:
Während der Transaktion gibt es 2 Bestellungen, nach ROLLBACK wieder 1: Die zweite ist verschwunden, als hätte es das INSERT nie gegeben.
Genauer, für Neugierige
Hier hält jedes Feld beim ersten Fehler an, wie die Konsole. Das Programm macht dasselbe mit try: Bei einer Exception schickt es ROLLBACK. Die Transaktion wird nicht von selbst abgebrochen, wenn eine Abfrage fehlschlägt, also ist ROLLBACK Sache des Programms.
Sieh es dir an
Die anderen Bestellungen
Die Bestellungen 2 bis 6 werden genauso geschrieben. Hier sind sie, am Ende mit der Zahl der Bestellungen pro Status:
Bestellung 4 ist storniert, 5 ist bezahlt und wartet auf die Lieferung, und 6 ist neu. Kalin (Kunde 5) hat noch nicht bestellt.
Übung
Der Preis aus dem Produkt
Die Seite kennt die Preise nicht auswendig. Wenn der Kunde kauft, wird der Preis in diesem Moment aus products geholt und in die Zeile geschrieben. INSERT … SELECT schreibt die Zeilen, die ein SELECT zurückgibt.
Kalin (Kunde 5) bestellt heute zwei Baguettes (Produkt 3). Schreibe die Bestellung (sie bekommt die Nummer 7, das Datum ist date('now')) und ihre Zeile, und hole unit_price mit INSERT … SELECT aus products.
Lösung
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;. Das SELECT gibt eine Zeile zurück: 7, 3, 2 und den Preis des Baguettes. Mit 1,80 von Hand stimmt der Preis nicht (das ist der Kaffee), und mit WHERE id = 2 gilt die Zeile dem Roggenbrot.
Die Idee
Die volle Datenbank
Die sechs Bestellungen sind in der Datenbank. Wie viele Zeilen jede Tabelle hat:
Die nächste Etappe stellt Fragen an diese Daten: die Speisekarte, die Rechnung jeder Bestellung, wer was kauft.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Constraints: Regeln für die Daten · Natürliche Schlüssel und Denormalisierung · ACID: alles oder nichts · BEGIN, COMMIT, ROLLBACK
- Aufgaben zum Thema: Projekt: Die Bäckerei von Grund auf