SQLite tiene el ON CONFLICT
cláusula que le permite especificar cómo manejar los conflictos de restricciones. Se aplica a UNIQUE
, NOT NULL
, CHECK
y PRIMARY KEY
restricciones (pero no FOREIGN KEY
restricciones).
Hay cinco opciones posibles que puede usar con esta cláusula:
ABORT
FAIL
IGNORE
REPLACE
ROLLBACK
Este artículo proporciona ejemplos y una explicación de cada una de estas opciones.
El ON CONFLICT
la cláusula se usa en CREATE TABLE
declaraciones, pero también se puede usar al insertar o actualizar datos reemplazando ON CONFLICT
con OR
.
Al crear la tabla
Como se mencionó, puede usar ON CONFLICT
cuando crea la tabla o cuando inserta/actualiza datos.
Aquí hay un ejemplo del uso de ON CONFLICT
en el momento de crear la tabla.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL ON CONFLICT IGNORE,
Price
);
Cuando usas el ON CONFLICT
cláusula, la aplica a la restricción específica que desea manejar. En este caso, agregué la cláusula a un NOT NULL
restricción.
En este caso especifiqué IGNORE
, lo que significa que, si hay una infracción de restricción, SQLite omitirá esa fila y luego continuará con el procesamiento.
Ahora, si trato de insertar NULL
en el Nombre del producto columna esa fila se omite.
INSERT INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Al insertar datos
También puede utilizar esta cláusula al insertar y actualizar datos. La diferencia es que reemplazas ON CONFLICT
con OR
.
Para demostrarlo, soltaré la tabla anterior y la crearé de nuevo, pero sin ON CONFLICT
cláusula:
DROP TABLE IF EXISTS Products;
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL,
Price
);
Ahora insertaré los mismos datos y usaré OR IGNORE
para omitir la fila que viola la restricción.
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Entonces obtenemos el mismo resultado que en el ejemplo anterior.
En estos ejemplos utilicé IGNORE
opción. Esta es solo una de las cinco opciones posibles para esta cláusula.
A continuación se muestran ejemplos que utilizan cada una de las cinco opciones.
Cancelar
Esta opción aborta la declaración SQL actual con un error SQLITE_CONSTRAINT y revierte cualquier cambio realizado por la declaración SQL actual; pero los cambios causados por declaraciones SQL anteriores dentro de la misma transacción se conservan y la transacción permanece activa.
Este es el comportamiento estándar. En otras palabras, esto es lo que sucede durante las violaciones de restricciones cuando no usas ON CONFLICT
cláusula.
Aquí hay un ejemplo de lo que sucede cuando especifica ABORT
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
No se devolvieron resultados porque INSERT
la operación se canceló y, por lo tanto, la tabla está vacía.
Esto es lo que sucede si pongo cada fila en su propio INSERT
declaración dentro de una transacción.
BEGIN TRANSACTION;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Fallo
El FAIL
La opción aborta la instrucción SQL actual con un error SQLITE_CONSTRAINT. Pero no revierte los cambios anteriores de la instrucción SQL que fallaron ni finaliza la transacción.
He aquí un ejemplo.
DELETE FROM Products;
INSERT OR FAIL INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99
Aquí está con INSERT
separado declaraciones dentro de una transacción.
DELETE FROM Products;
BEGIN TRANSACTION;
INSERT OR FAIL INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR FAIL INTO Products VALUES (2, NULL, 1.49);
INSERT OR FAIL INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR FAIL INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR FAIL INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR FAIL INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Ignorar
El IGNORE
La opción salta la única fila que contiene la violación de la restricción y continúa procesando las filas subsiguientes de la instrucción SQL como si nada hubiera salido mal. Otras filas antes y después de la fila que contenía la infracción de restricción se insertan o actualizan normalmente. No se devuelve ningún error por singularidad, NOT NULL
y UNIQUE
errores de restricción cuando se utiliza esta opción. Sin embargo, esta opción funciona como ABORT
para errores de restricción de clave externa.
Los primeros ejemplos en esta página usan IGNORE
, pero aquí está de nuevo.
DELETE FROM Products;
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Reemplazar
El REPLACE
La opción funciona de manera diferente dependiendo de la infracción:
- Cuando un
UNIQUE
oPRIMARY KEY
se produce una violación de la restricción, elREPLACE
La opción elimina las filas preexistentes que causan la violación de la restricción antes de insertar o actualizar la fila actual y el comando continúa ejecutándose normalmente. - Si un
NOT NULL
se produce una violación de la restricción, reemplaza elNULL
valor con el valor predeterminado para esa columna, o si la columna no tiene un valor predeterminado, entonces elABORT
se utiliza el algoritmo. - Si un
CHECK
se produce una violación de la restricción o de la restricción de clave externa, entoncesREPLACE
funciona comoABORT
.
Además, si elimina filas para satisfacer una restricción, los activadores de eliminación se activan solo si los activadores recursivos están habilitados.
Aquí hay un ejemplo que usa el REPLACE
opción.
DELETE FROM Products;
INSERT OR REPLACE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, 'Nails', 1.49),
(3, 'Saw', 11.34),
(1, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 37.0 2 Nails 1.49 3 Saw 11.34 5 Chisel 23.0 6 Bandage 120.0
En este ejemplo, el conflicto fue con la clave principal (traté de insertar dos filas con el mismo ProductId ). El REPLACE
opción hizo que la segunda reemplazara a la primera.
Restaurar
Otra opción es usar ROLLBACK
.
Esta opción aborta la instrucción SQL actual con un error SQLITE_CONSTRAINT y revierte la transacción actual. Si no hay ninguna transacción activa (aparte de la transacción implícita que se crea en cada comando), entonces funciona igual que ABORT
algoritmo.
Aquí hay un ejemplo que usa múltiples INSERT OR ROLLBACK
declaraciones dentro de una transacción.
DELETE FROM Products;
BEGIN TRANSACTION;
INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Aquí está el resultado completo de mi terminal cuando ejecuto esto:
sqlite> DELETE FROM Products; sqlite> sqlite> BEGIN TRANSACTION; sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00); sqlite> COMMIT; Error: cannot commit - no transaction is active sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Entonces llegó a la violación de la restricción, luego revirtió la transacción. Luego se procesaron las líneas subsiguientes y luego el COMMIT
se encontró la palabra clave. Para entonces, la transacción ya se había revertido, por lo que recibimos otro error que nos decía que no había ninguna transacción activa.
Esto es lo que sucede si lo elimino de la transacción.
DELETE FROM Products;
INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00);
SELECT * FROM Products;
Aquí está el resultado completo de mi terminal cuando ejecuto esto:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00); sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
En este caso, funcionó como ABORT
.
Para confirmar, aquí está la misma declaración usando ABORT
en lugar de ROLLBACK
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00);
SELECT * FROM Products;
Aquí está el resultado completo de mi terminal cuando ejecuto esto:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00); sqlite> INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00); sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0