sql >> Base de Datos >  >> RDS >> SQLite

Cómo funciona ON CONFLICT en SQLite

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 o PRIMARY KEY se produce una violación de la restricción, el REPLACE 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 el NULL valor con el valor predeterminado para esa columna, o si la columna no tiene un valor predeterminado, entonces el ABORT se utiliza el algoritmo.
  • Si un CHECK se produce una violación de la restricción o de la restricción de clave externa, entonces REPLACE funciona como ABORT .

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