La panadería, etapa 2: los datos
En resumen: La etapa 2 llena las tablas de la panadería con INSERT, RETURNING e INSERT … SELECT, y guarda el primer pedido dentro de una transacción.
Problema
Tablas vacías
Las tablas de la etapa 1 están listas y vacías. La panadería tiene tres categorías, ocho productos, cinco clientes y sus primeros pedidos. En esta etapa los escribes, y cada regla de la etapa 1 vigilará los datos.
Todos los campos parten de la base que has construido hasta ahora: las cinco tablas y los datos de los pasos anteriores de la etapa.
La idea
La primera fila
INSERT INTO tabla (columnas) VALUES (valores) escribe una fila. La columna id no está en la lista: INTEGER PRIMARY KEY sin valor recibe por sí sola el siguiente número.
Las columnas entre paréntesis importan. INSERT INTO categories VALUES (1, 'Pan') también funciona, pero depende del orden de las columnas de la tabla y se rompe con la primera columna nueva.
Pruébalo
Las tres categorías
Escribe las tres categorías con un solo INSERT: Pan, Dulces y Bebidas, en este orden (Pan es 1, Dulces 2, Bebidas 3).
Solución
INSERT INTO categories (name) VALUES
('Pan'),
('Dulces'),
('Bebidas');Varias filas en VALUES, separadas por comas: ('Pan'), ('Dulces'), ('Bebidas'). El orden de escritura da los números. Con las bebidas antes que los dulces, las bebidas pasan a ser 2, y los productos apuntarán después a la categoría equivocada.
La idea
Lo que escribió la base
El programa a menudo necesita el número de la fila nueva, por ejemplo para mostrar «El producto se añadió con el número 1». RETURNING devuelve las columnas de la fila escrita:
RETURNING también devuelve los valores que la base puso por sí sola: sin stock en la lista devolvería 0, que viene de DEFAULT. Los ocho productos se escriben con un solo INSERT:
Más precisamente, para curiosos
RETURNING existe en SQLite desde la versión 3.35 y en PostgreSQL. MySQL no lo tiene: allí el número se toma con LAST_INSERT_ID(), y en SQLite también con last_insert_rowid().
Míralo
Las reglas vigilan
Las reglas de la etapa 1 ya funcionan. Un producto con precio 0:
Un producto en la categoría 9, que no existe:
Un producto sin nombre:
Ninguno de los tres entra. El error viene de la base, así que da igual quién envíe la fila: el sitio, un script de importación desde Excel o un compañero en la consola.
Pruébalo
Los clientes
Escribe los cinco clientes con un solo INSERT (columnas name, email, city): Ivan Kolev, ivan.kolev@example.com, Sofía; Ana Petrova, ana.petrova@example.com, Plovdiv; Boris Nikolov, boris.nikolov@example.com, Sofía; Desislava Marinova, desislava.marinova@example.com, Varna; Kalin Todorov, kalin.todorov@example.com, sin ciudad.
Solución
INSERT INTO customers (name, email, city) VALUES
('Ivan Kolev', 'ivan.kolev@example.com', 'Sofía'),
('Ana Petrova', 'ana.petrova@example.com', 'Plovdiv'),
('Boris Nikolov', 'boris.nikolov@example.com', 'Sofía'),
('Desislava Marinova', 'desislava.marinova@example.com', 'Varna'),
('Kalin Todorov', 'kalin.todorov@example.com', NULL);La ciudad de Kalin es NULL, sin comillas. '' es texto vacío: una ciudad que se conoce y está vacía, no «no lo sabemos». Entonces WHERE city IS NULL no encontraría a Kalin. Los correos se escriben exactamente así, en minúsculas.
Más precisamente, para curiosos
UNIQUE compara el texto letra por letra: Ivan.Kolev@example.com e ivan.kolev@example.com son dos correos distintos para la base. Por eso el programa pasa el correo a minúsculas antes de escribirlo, o la tabla mantiene UNIQUE sobre lower(email) con un índice (CREATE UNIQUE INDEX … ON customers (lower(email))).
Míralo
El mismo correo
Ivan de Plovdiv es otra persona con el mismo correo, y la base no lo deja entrar. El registro en el sitio muestra justo este error como «Este correo ya está registrado».
La idea
El pedido y sus líneas
Un pedido sin status recibe 'new' de DEFAULT:
Un pedido real, sin embargo, son dos tipos de filas: una en orders y varias en order_items. Si el programa se cae entre ambas, queda un pedido sin un solo producto. Por eso las dos se escriben en una transacción: BEGIN, los insert, COMMIT. Hasta el COMMIT nada es definitivo.
Pruébalo
El primer pedido
Ivan (cliente 1) pidió el 2026-10-01 dos panes de campo (producto 1 a 2,40) y tres cruasanes (producto 4 a 1,30). El pedido está entregado. Escríbelo en una transacción: el pedido (tendrá el número 1) y sus dos líneas.
Solución
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');, luego INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (1, 1, 2, 2.40), (1, 4, 3, 1.30); y COMMIT;. Una línea con el pedido 2 la detiene la clave foránea: ese pedido todavía no existe. El cruasán a 2,40 es el precio del pan, no el suyo.
Míralo
Cancelar
El programa ha escrito el pedido y descubre que algo va mal (la tarjeta fue rechazada, una línea se detuvo con un error). Entonces, en lugar de COMMIT, envía ROLLBACK:
Durante la transacción hay 2 pedidos, y después del ROLLBACK otra vez 1: el segundo desapareció, como si el INSERT nunca hubiera existido.
Más precisamente, para curiosos
Aquí cada campo se detiene en el primer error, como la consola. El programa hace lo mismo con try: ante una excepción envía ROLLBACK. La transacción no se cancela sola cuando falla una consulta, así que el ROLLBACK es cosa del programa.
Míralo
Los otros pedidos
Los pedidos del 2 al 6 se escriben igual. Aquí están, con el número de pedidos por estado al final:
El pedido 4 está cancelado, el 5 está pagado y espera la entrega, y el 6 es nuevo. Kalin (cliente 5) todavía no ha pedido.
Ejercicio
El precio del producto
El sitio no se sabe los precios de memoria. Cuando el cliente compra, el precio se toma de products en ese momento y se escribe en la línea. INSERT … SELECT escribe las filas que devuelve un SELECT.
Kalin (cliente 5) pide hoy dos baguettes (producto 3). Escribe el pedido (será el número 7, la fecha es date('now')) y su línea, tomando unit_price de products con INSERT … SELECT.
Solución
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;. El SELECT devuelve una fila: 7, 3, 2 y el precio de la baguette. Con 1,80 a mano el precio es incorrecto (ese es el del café), y con WHERE id = 2 la línea es del pan de centeno.
La idea
La base completa
Los seis pedidos están en la base. Cuántas filas tiene cada tabla:
La siguiente etapa hace preguntas a estos datos: la carta, la cuenta de cada pedido, quién compra qué.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Restricciones: reglas para los datos · Claves naturales y desnormalización · ACID: todo o nada · BEGIN, COMMIT, ROLLBACK
- Ejercicios sobre el tema: Proyecto: la panadería desde cero