SQLite
 sql >> Base de Dados >  >> RDS >> SQLite

Como ON CONFLICT funciona no SQLite


SQLite tem o ON CONFLICT cláusula que permite especificar como lidar com conflitos de restrição. Aplica-se a UNIQUE , NOT NULL , CHECK e PRIMARY KEY restrições (mas não FOREIGN KEY restrições).

Existem cinco opções possíveis que você pode usar com esta cláusula:
  • ABORT
  • FAIL
  • IGNORE
  • REPLACE
  • ROLLBACK

Este artigo fornece exemplos e uma explicação de cada uma dessas opções.

O ON CONFLICT cláusula é usada em CREATE TABLE instruções, mas também pode ser usado ao inserir ou atualizar dados substituindo ON CONFLICT com OR .

Ao criar a tabela


Como mencionado, você pode usar ON CONFLICT ao criar a tabela ou ao inserir/atualizar dados.

Aqui está um exemplo de uso de ON CONFLICT no momento da criação da tabela.
CREATE TABLE Products( 
    ProductId INTEGER PRIMARY KEY, 
    ProductName NOT NULL ON CONFLICT IGNORE, 
    Price
); 

Quando você usa o ON CONFLICT cláusula, você a aplica à restrição específica que deseja manipular. Nesse caso, adicionei a cláusula a um NOT NULL limitação.

Neste caso eu especifiquei IGNORE , o que significa que, se houver uma violação de restrição, o SQLite pulará essa linha e continuará o processamento.

Agora, se eu tentar inserir NULL no ProductName coluna essa linha é ignorada.
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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120.0 

Ao inserir dados


Você também pode usar esta cláusula ao inserir e atualizar dados. A diferença é que você substitui ON CONFLICT com OR .

Para demonstrar, vou descartar a tabela anterior e criá-la novamente, mas sem o ON CONFLICT cláusula:
DROP TABLE IF EXISTS Products;

CREATE TABLE Products( 
    ProductId INTEGER PRIMARY KEY, 
    ProductName NOT NULL, 
    Price
); 

Agora vou inserir os mesmos dados e usar OR IGNORE para pular a linha que viola a restrição.
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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120.0 

Assim, obtemos o mesmo resultado do exemplo anterior.

Nestes exemplos eu usei o IGNORE opção. Esta é apenas uma das cinco opções possíveis para esta cláusula.

Abaixo estão exemplos usando cada uma das cinco opções.

Abortar


Esta opção aborta a instrução SQL atual com um erro SQLITE_CONSTRAINT e desfaz quaisquer alterações feitas pela instrução SQL atual; mas as alterações causadas por instruções SQL anteriores na mesma transação são preservadas e a transação permanece ativa.

Este é o comportamento padrão. Em outras palavras, isso é o que acontece durante violações de restrição quando você não usa o ON CONFLICT cláusula.

Aqui está um exemplo do que acontece quando você 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:
 
Nenhum resultado foi retornado porque o INSERT operação foi abortada e, portanto, a tabela está vazia.

Aqui está o que acontece se eu colocar cada linha em seu próprio INSERT declaração dentro de uma transação.
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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120.0 

Falha


O FAIL A opção aborta a instrução SQL atual com um erro SQLITE_CONSTRAINT. Mas ele não desfaz as alterações anteriores da instrução SQL que falhou nem encerra a transação.

Aqui está um exemplo.
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 Martelo 9,99 

Aqui está com INSERT separado declarações dentro de uma transação.
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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120.0 

Ignorar


O IGNORE A opção ignora a linha que contém a violação de restrição e continua processando as linhas subsequentes da instrução SQL como se nada tivesse dado errado. Outras linhas antes e depois da linha que continha a violação de restrição são inseridas ou atualizadas normalmente. Nenhum erro é retornado por exclusividade, NOT NULL e UNIQUE erros de restrição quando esta opção é usada. No entanto, esta opção funciona como ABORT para erros de restrição de chave estrangeira.

Os primeiros exemplos nesta página usam IGNORE , mas aqui está novamente.
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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120.0 

Substituir


O REPLACE opção funciona de forma diferente dependendo da violação:
  • Quando um UNIQUE ou PRIMARY KEY ocorre violação de restrição, o REPLACE A opção exclui linhas pré-existentes que estão causando a violação de restrição antes de inserir ou atualizar a linha atual e o comando continua sendo executado normalmente.
  • Se um NOT NULL ocorre violação de restrição, ele substitui o NULL value com o valor padrão para essa coluna, ou se a coluna não tiver valor padrão, então o ABORT algoritmo é usado.
  • Se um CHECK ocorre uma violação de restrição ou restrição de chave estrangeira, então REPLACE funciona como ABORT .

Além disso, se ele excluir linhas para satisfazer uma restrição, os gatilhos de exclusão serão acionados se e somente se os gatilhos recursivos estiverem ativados.

Aqui está um exemplo que usa o REPLACE opção.
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 Chave 37,0 2 Pregos 1,49 3 Serra 11,34 5 Cinzel 23,0 6 Bandagem 120.0 

Neste exemplo, o conflito foi com a chave primária (tentei inserir duas linhas com o mesmo ProductId ). O REPLACE opção fez com que o segundo substituísse o primeiro.

Reversão


Outra opção é usar ROLLBACK .

Esta opção anula a instrução SQL atual com um erro SQLITE_CONSTRAINT e reverte a transação atual. Se nenhuma transação estiver ativa (além da transação implícita que é criada em cada comando), ela funcionará da mesma forma que ABORT algoritmo.

Aqui está um exemplo que usa vários INSERT OR ROLLBACK declarações dentro de uma transação.
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; 

Aqui está a saída completa do meu terminal quando executo isso:
sqlite> DELETE FROM Products;sqlite> sqlite> BEGIN TRANSACTION;sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Martelo', 9.99);sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Erro:Falha na restrição NOT NULL:Products.ProductNamesqlite> 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;Erro:não pode confirmar - nenhuma transação está ativasqlite> sqlite> SELECT * FROM Produtos;ProductId ProductName Price ---------- ----------- ----------3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120,0  
Então, chegou à violação de restrição e, em seguida, reverteu a transação. Em seguida, as linhas subsequentes foram processadas e, em seguida, o COMMIT palavra-chave foi encontrada. Até então, a transação já havia sido revertida e, portanto, recebemos outro erro nos informando que nenhuma transação estava ativa.

Aqui está o que acontece se eu removê-lo da transação.
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; 

Aqui está a saída completa do meu terminal quando executo isso:
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);Erro:NOT NULL constraint falhou:Products.ProductNamesqlite> 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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120,0 

Nesse caso, funcionou como ABORT .

Para confirmar, aqui está a mesma declaração usando ABORT em vez 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; 

Aqui está a saída completa do meu terminal quando executo isso:
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);Erro:NOT NULL constraint falhou:Products.ProductNamesqlite> 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 Martelo 9,99 3 Serra 11,34 4 Chave 37,0 5 Cinzel 23,0 6 Bandagem 120,0