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

Lidando com conflitos de chave primária ao inserir dados no SQLite


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