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
ouPRIMARY KEY
ocorre violação de restrição, oREPLACE
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 oNULL
value com o valor padrão para essa coluna, ou se a coluna não tiver valor padrão, então oABORT
algoritmo é usado. - Se um
CHECK
ocorre uma violação de restrição ou restrição de chave estrangeira, entãoREPLACE
funciona comoABORT
.
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, oCOMMIT
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 comoABORT
.
Para confirmar, aqui está a mesma declaração usandoABORT
em vez deROLLBACK
.
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