SQLite tem uma cláusula de extensão SQL não padrão chamada
ON CONFLICT
que nos permite especificar como lidar com conflitos de restrição. Em particular, a cláusula se aplica a
UNIQUE
, NOT NULL
, CHECK
e PRIMARY KEY
restrições. Este artigo fornece exemplos de como essa cláusula pode ser usada para determinar como lidar com conflitos de restrição de chave primária.
Por “conflitos de restrição de chave primária”, quero dizer quando você tenta inserir um valor duplicado em uma coluna de chave primária. Por padrão, ao tentar fazer isso, a operação será abortada e o SQLite retornará um erro.
Mas você pode usar o
ON CONFLICT
cláusula para mudar a forma como o SQLite lida com essas situações. Uma opção é usar esta cláusula no
CREATE TABLE
declaração ao criar a tabela. Isso determinará como todos os INSERT
operações são tratadas. Outra opção é usar a cláusula no
INSERT
declaração sempre que você tenta inserir dados na tabela. Isso permite que você aproveite a cláusula mesmo quando a tabela não foi criada com ela. Quando você usa essa opção, a sintaxe é diferente; você usa OR
em vez de ON CONFLICT
. Os exemplos nesta página usam a segunda opção – eu crio a tabela sem o
ON CONFLICT
cláusula e, em vez disso, especifico OR
no INSERT
demonstração. Tabela de amostra
Vamos criar uma tabela simples e adicionar uma linha.
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
Atualmente, temos uma linha, com um ProductId de 1 .
Agora podemos percorrer os vários cenários de inserção de dados nessa tabela que violam a restrição de chave primária.
Exemplo 1 – Abortar (comportamento padrão)
Como mencionado, o comportamento padrão do SQLite é abortar o
INSERT
operação e retornar um erro. INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Resultado:
Error: UNIQUE constraint failed: Products.ProductId
Um erro foi retornado e nada foi inserido.
Isso é o equivalente a usar o
OR ABORT
opção. INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Resultado:
Error: UNIQUE constraint failed: Products.ProductId
Podemos verificar se nada foi inserido executando um
SELECT
declaração contra a mesa. SELECT * FROM Products;
Resultado:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Podemos ver que a tabela contém apenas a linha original.
Exemplo 2 – Ignorar
Uma alternativa é fazer com que o SQLite ignore a linha incorreta. Em outras palavras, ele pulará a linha e continuará processando as linhas subsequentes.
Para fazer isso em seu
INSERT
declaração, use OR IGNORE
. O efeito disso é que o
INSERT
a operação for bem-sucedida, mas sem nenhuma linha que violem a restrição de chave primária. 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
Nesse caso tentei inserir duas novas linhas com um ID que já existia na tabela, então ambas as linhas foram ignoradas.
Exemplo 3 – Substituir
Outra opção que você tem é substituir a linha original pela nova linha.
Em outras palavras, você substituirá os dados existentes pelos novos dados.
Para fazer isso, 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
Nesse caso, a maioria das linhas era a mesma, portanto, elas contêm os mesmos dados após o
INSERT
Operação. No entanto, podemos ver que a primeira linha foi atualizada para usar os valores em meu INSERT
demonstração. Também podemos ver que ele usou o segundo conjunto de valores (visto que dois compartilharam o mesmo ProductId ).
Então o efeito é como um
UPDATE
instrução e INSERT
declaração combinada. Exemplo 4 – Reversão
Outra opção é usar o
ROLLBACK
opção. Isso 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. Vale a pena estar atento a como esta opção funciona. 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', 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;
Aqui está a saída completa do meu terminal quando executo isso:
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>
Basicamente, o que aconteceu aqui é que chegou até a violação de restrição e, em seguida, reverteu a transação. Então as próximas duas linhas foram processadas e então 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', 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;
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', 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>
Nesse caso, funcionou como
ABORT
. Para demonstrar isso, aqui está a mesma declaração usando
ABORT
em vez 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;
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', 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>
A opção de falha
O
FAIL
A opção aborta a instrução SQL atual com um erro SQLITE_CONSTRAINT. Mas essa opção não desfaz as alterações anteriores da instrução SQL que falhou nem encerra a transação. 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