UPSERT (ON CONFLICT) y aplicaciones de INSERT masivo
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.
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.
Antes de los ejercicios, comprueba las definiciones de columnas y los datos de ejemplo de las dos tablas — stock y stock_in.
① Ejecuta PRAGMA table_info(stock); para ver nombres de columnas, tipos y la clave primaria de stock (UPSERT asume que sku es la clave primaria).
② Previsualiza las dos tablas con SELECT * FROM stock ORDER BY sku; y SELECT * FROM stock_in;.
Editor SQL
Ejecutar una consulta para ver el resultado
Esquema
Sin tablas
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.
Ramificación de ON CONFLICT DO UPDATELa fila del INSERT o bien choca con la clave primaria sku o bien no. En caso de colisión, DO UPDATE actualiza la fila existente; sin colisión, la fila se inserta como nueva.
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 qtyINSERT INTO stock(sku, name, qty, price)VALUES ('A001', 'Pen', 5, 80)ON CONFLICT(sku) DO UPDATESET qty = qty + excluded.qty;-- Sku nuevo → sin colisión, simplemente se insertaINSERT INTO stock(sku, name, qty, price)VALUES ('A006', 'Marker', 3, 120)ON CONFLICT(sku) DO UPDATESET qty = qty + excluded.qty;SELECT sku, name, qty FROM stock WHERE sku IN ('A001', 'A006');
Imagina el requisito: "en el procesado de entradas, suma al stock para los skus existentes y registra los nuevos para los skus desconocidos". (Ejecútalo correctamente y aparecerá la explicación.)
① Antes del UPSERT, ejecuta SELECT sku, name, qty FROM stock WHERE sku IN ('A001','A006') ORDER BY sku; para confirmar que A001 existe y A006 no.
② Para el A001 existente, haz UPSERT con name Pen, qty 15, price 80. En caso de colisión, suma el valor insertado al qty existente (no sobrescribas).
③ Para el A006 desconocido, ejecuta el mismo tipo de UPSERT con name Stapler, qty 3, price 200 (sin colisión, así que se inserta).
④ Por último, vuelve a ejecutar el mismo SELECT y confirma que se sumó a A001 y se insertó A006.
Editor SQL
Ejecutar una consulta para ver el resultado
Esquema
Sin tablas
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.
Comportamiento de DO NOTHINGLas filas que no chocan se insertan con normalidad; las colisiones se saltan en silencio, sin error. Los valores existentes nunca cambian.
-- 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';
Imagina el requisito: "al cargar varias filas en el maestro de stock, no sobrescribas nunca los skus existentes y añade solo los desconocidos".
① Antes del UPSERT, ejecuta SELECT sku, name, qty, price FROM stock WHERE sku IN ('A003','A007') ORDER BY sku; para confirmar que A003 existe y A007 no.
② Intenta insertar el A003 existente con name Clip, qty 0, price 0, usando DO NOTHING para que se ignore en caso de colisión.
③ Añade el A007 desconocido con name Eraser, qty 50, price 90 usando el mismo UPSERT con DO NOTHING.
④ Por último, vuelve a ejecutar el mismo SELECT y confirma que A003 no cambia y que se añadió A007.
Editor SQL
Ejecutar una consulta para ver el resultado
Esquema
Sin tablas
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).
Ramificación por fila de un UPSERT multifilaCada fila de VALUES se comprueba de forma independiente contra la clave sku; las colisiones pasan por DO UPDATE para acumular, las filas sin colisión se insertan como nuevas.
-- UPSERT multifila: las filas existentes acumulan qty y refrescan price; las nuevas se insertanINSERT INTO stock(sku, name, qty, price)VALUES ('A002', 'Note', 10, 260), ('A005', 'Glue', 20, 190), ('A007', 'Ruler', 15, 90)ON CONFLICT(sku) DO UPDATESET qty = qty + excluded.qty, price =excluded.price;SELECT sku, name, qty, price FROM stock ORDER BY sku;
Imagina el requisito: "refleja las 3 filas de la tabla de entradas stock_in en stock con una sola sentencia". Este es el ejercicio final del artículo.
① Antes del UPSERT, ejecuta SELECT sku, name, qty FROM stock WHERE sku IN ('A001','A004','A006') ORDER BY sku; para confirmar que A001 / A004 existen y A006 no.
② Contra stock, inserta 3 filas en un único INSERT — A001 (name Pen / qty 50 / price 80), A004 (name Tape / qty 100 / price 150), A006 (name Marker / qty 30 / price 120) — y escribe un UPSERT masivo con ON CONFLICT(sku) DO UPDATE que sume alqty en caso de colisión.
③ Por último, coloca SELECT sku, name, qty FROM stock ORDER BY sku; y confirma que se sumó a A001 / A004 y que A006 se añadió nuevo.
Editor SQL
Ejecutar una consulta para ver el resultado
Esquema
Sin tablas
QUIZ
Verificación de conocimientos
Responde cada pregunta una a una.
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?
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?