Pregunta 1En INSERT INTO stock(sku, qty) VALUES ('A001', 15) ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;, con A001 ya existente (qty 120), ¿cuál es el qty tras el UPSERT?
UPSERT (ON CONFLICT) y aplicaciones de INSERT masivo
Este artículo forma parte del Curso de SQL, con el que dominarás desde cero habilidades prácticas de SQL, desde lo básico hasta las consultas complejas y la optimización de SQL.
Usa INSERT … ON CONFLICT(sku) DO UPDATE sobre la tabla stock para sumar excluded.qty al A001 existente, insertar un A006 nuevo, proteger las filas existentes con DO NOTHING y hacer un UPSERT masivo de las 3 filas de stock_in — todo práctico.
Los datos que vamos a usar — stock y stock_in
UPSERT (una mezcla de UPDATE e INSERT —
la operación "actualizar si existe, insertar si no" en una sola sentencia) se implementa mediante la sintaxis INSERT … ON CONFLICT(clave) DO UPDATE.
Si la fila choca con una clave primaria o una restricción UNIQUE, se ejecuta el update; si no, se inserta tal cual.
ON CONFLICT DO UPDATE — actualizar si existe, insertar si no
Escribir INSERT INTO tabla(...) VALUES (...) ON CONFLICT(col_clave) DO UPDATE SET col = ... significa: si la fila que intentas insertar choca con la clave declarada en ON CONFLICT (la clave primaria o una columna UNIQUE), se ejecuta el update de DO UPDATE; si no hay colisión, la fila simplemente se inserta.
Dentro de DO UPDATE, un nombre de tabla especial excluded te permite referenciar "los valores de la fila que intentaste insertar".
Escribe qty = qty + excluded.qty y, en caso de colisión, el qty existente se incrementa con el qty que intentabas insertar.
Usa qty = excluded.qty para sobrescribir y qty = qty + excluded.qty para acumular, según lo que necesites.
-- Choca con sku existente → DO UPDATE suma al qty
INSERT INTO stock(sku, name, qty, price)
VALUES ('A001', 'Pen', 5, 80)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
-- Sku nuevo → sin colisión, simplemente se inserta
INSERT INTO stock(sku, name, qty, price)
VALUES ('A006', 'Marker', 3, 120)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
SELECT sku, name, qty FROM stock WHERE sku IN ('A001', 'A006');
DO NOTHING — no hacer nada en caso de colisión
Cuando no quieres ni actualizar ni insertar — es decir, "saltar en silencio en caso de colisión" — usa ON CONFLICT(clave) DO NOTHING.
Sin colisión la fila se inserta; en colisión la fila se ignora sin error.
En el ejemplo siguiente, intentar insertar el A002 existente con DO NOTHING choca y se ignora, mientras que el nuevo A007 se inserta.
-- Ignorar en caso de colisión (DO NOTHING)
INSERT INTO stock(sku, name, qty, price)
VALUES ('A002', 'Note', 999, 999)
ON CONFLICT(sku) DO NOTHING;
-- A002 conserva sus valores originales (qty 60 / price 250)
SELECT sku, name, qty, price FROM stock WHERE sku = 'A002';
UPSERT multifila — combina INSERT masivo con ON CONFLICT
Cuando añades ON CONFLICT a un INSERT multifila — VALUES (...),(...),(...) separados por comas — cada fila, de forma independiente, "actualiza en colisión, inserta si no".
Puedes reflejar datos de entradas en una sola sentencia, reemplazando un bucle procedimental que ramifica entre UPDATE e INSERT por fila con un único SQL.
Incluso en un UPSERT multifila, excluded sigue significando "el valor que intentaste insertar para esta fila", así que escribir qty = qty + excluded.qty aplica "sumar para las filas existentes, insertar tal cual para las nuevas" fila a fila.
El ejercicio final de este artículo es un UPSERT masivo de las 3 filas de stock_in (A001 / A004 / A006).
-- UPSERT multifila: las filas existentes acumulan qty y refrescan price; las nuevas se insertan
INSERT INTO stock(sku, name, qty, price)
VALUES
('A002', 'Note', 10, 260),
('A005', 'Glue', 20, 190),
('A007', 'Ruler', 15, 90)
ON CONFLICT(sku) DO UPDATE
SET qty = qty + excluded.qty,
price = excluded.price;
SELECT sku, name, qty, price FROM stock ORDER BY sku;
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿A qué se refiere excluded dentro del DO UPDATE de un UPSERT?
Pregunta 3¿Qué sintaxis usas para nunca modificar los datos existentes y saltar en silencio las colisiones, sin error?