SQLite tiene una cláusula de extensión de SQL no estándar llamada ON CONFLICT
que nos permite especificar cómo tratar los conflictos de restricciones.
En particular, la cláusula se aplica a UNIQUE
, NOT NULL
, CHECK
y PRIMARY KEY
restricciones.
Este artículo proporciona ejemplos de cómo se puede usar esta cláusula para determinar cómo manejar los conflictos de restricción de clave principal.
Por "conflictos de restricción de clave principal", me refiero a cuando intenta insertar un valor duplicado en una columna de clave principal. De manera predeterminada, cuando intente hacer esto, la operación se cancelará y SQLite devolverá un error.
Pero puedes usar el ON CONFLICT
cláusula para cambiar la forma en que SQLite trata estas situaciones.
Una opción es usar esta cláusula en el CREATE TABLE
declaración al crear la tabla. Hacer eso determinará cómo todos INSERT
se tratan las operaciones.
Otra opción es usar la cláusula en INSERT
cada vez que intenta insertar datos en la tabla. Esto le permite aprovechar la cláusula incluso cuando la tabla no se creó con ella. Cuando usa esta opción, la sintaxis es diferente; usas OR
en lugar de ON CONFLICT
.
Los ejemplos en esta página usan la segunda opción:creo la tabla sin el ON CONFLICT
cláusula, y en su lugar especifico OR
en el INSERT
declaración.
Tabla de muestra
Creemos una tabla simple y agreguemos una fila.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Actualmente tenemos una fila, con un ProductId de 1 .
Ahora podemos ejecutar los diversos escenarios de inserción de datos en esa tabla que viola la restricción de clave principal.
Ejemplo 1:cancelar (comportamiento predeterminado)
Como se mencionó, el comportamiento predeterminado para SQLite es abortar INSERT
operación y devolver un error.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Resultado:
Error: UNIQUE constraint failed: Products.ProductId
Se devolvió un error y no se insertó nada.
Este es el equivalente de usar OR ABORT
opción.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Resultado:
Error: UNIQUE constraint failed: Products.ProductId
Podemos verificar que no se insertó nada ejecutando un SELECT
declaración contra la mesa.
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Podemos ver que la tabla solo contiene la fila original.
Ejemplo 2:ignorar
Una alternativa es hacer que SQLite ignore la fila infractora. En otras palabras, saltará la fila y continuará procesando las filas subsiguientes.
Para hacer esto dentro de su INSERT
declaración, use OR IGNORE
.
El efecto de esto es que INSERT
la operación tiene éxito, pero sin ninguna fila que viole la restricción de clave principal.
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 12.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
En este caso, traté de insertar dos filas nuevas con una identificación que ya existía en la tabla, por lo que se omitieron ambas filas.
Ejemplo 3:reemplazar
Otra opción que tiene es reemplazar la fila original con la nueva fila.
En otras palabras, sobrescribirá los datos existentes con sus nuevos datos.
Para hacer esto, use OR REPLACE
.
INSERT OR REPLACE INTO Products VALUES
(1, 'Hammer', 12.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
En este caso, la mayoría de las filas eran iguales, por lo que contienen los mismos datos después de INSERT
operación. Sin embargo, podemos ver que la primera fila se actualizó para usar los valores en mi INSERT
declaración.
También podemos ver que usó el segundo conjunto de valores (ya que dos compartían el mismo ProductId ).
Así que el efecto es algo así como una UPDATE
instrucción y INSERT
declaración combinada.
Ejemplo 4:reversión
Otra opción es usar el ROLLBACK
opción.
Esto 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.
Vale la pena tener en cuenta cómo funciona esta opción. 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', 8.00);
INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50);
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> BEGIN TRANSACTION; sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 8.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId 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 ---------- ----------- ---------- 5 Chisel 23.0 6 Bandage 120.0 sqlite>
Básicamente, lo que sucedió aquí es que llegó hasta la violación de la restricción y luego revirtió la transacción. Luego se procesaron las siguientes dos líneas 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', 8.00);
INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50);
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', 8.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId 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 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0 sqlite>
En este caso, funcionó como ABORT
.
Para demostrar esto, aquí está la misma declaración usando ABORT
en lugar de ROLLBACK
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 8.00);
INSERT OR ABORT INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 22.50);
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', 8.00); sqlite> INSERT OR ABORT INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ABORT INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId 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 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0 sqlite>
La opción del fracaso
El FAIL
La opción aborta la instrucción SQL actual con un error SQLITE_CONSTRAINT. Pero esta opción no revierte los cambios anteriores de la instrucción SQL que falló ni finaliza la transacción.
DELETE FROM Products;
INSERT OR FAIL INTO Products VALUES
(1, 'Hammer', 8.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5