SQLite har en icke-standardiserad SQL-tilläggssats som heter ON CONFLICT
som gör det möjligt för oss att specificera hur vi ska hantera tvångskonflikter.
I synnerhet gäller klausulen UNIQUE
, NOT NULL
, CHECK
och PRIMARY KEY
begränsningar.
Den här artikeln ger exempel på hur den här klausulen kan användas för att avgöra hur konflikter med primärnyckelbegränsningar ska hanteras.
Med "konflikter med primärnyckelbegränsningar" menar jag när du försöker infoga ett dubblettvärde i en primärnyckelkolumn. Som standard, när du försöker göra detta, kommer operationen att avbrytas och SQLite returnerar ett fel.
Men du kan använda ON CONFLICT
klausul för att ändra hur SQLite hanterar dessa situationer.
Ett alternativ är att använda denna sats i CREATE TABLE
uttalande när du skapar tabellen. Att göra det kommer att avgöra hur alla INSERT
operationer behandlas.
Ett annat alternativ är att använda satsen på INSERT
uttalande när du försöker infoga data i tabellen. Detta gör att du kan dra fördel av klausulen även när tabellen inte skapades med den. När du använder det här alternativet är syntaxen annorlunda; du använder OR
istället för ON CONFLICT
.
Exemplen på den här sidan använder det andra alternativet – jag skapar tabellen utan koden ON CONFLICT
sats, och jag anger istället OR
på INSERT
uttalande.
Exempeltabell
Låt oss skapa en enkel tabell och lägga till en rad.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Resultat:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Vi har för närvarande en rad med ett ProductId av 1 .
Nu kan vi gå igenom de olika scenarierna för att infoga data i den tabellen som bryter mot den primära nyckelbegränsningen.
Exempel 1 – Avbryt (standardbeteende)
Som nämnts är standardbeteendet för SQLite att avbryta INSERT
operation och returnerar ett fel.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Resultat:
Error: UNIQUE constraint failed: Products.ProductId
Ett fel returnerades och ingenting infogades.
Detta motsvarar att använda OR ABORT
alternativ.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Resultat:
Error: UNIQUE constraint failed: Products.ProductId
Vi kan verifiera att inget har infogats genom att köra en SELECT
uttalande mot bordet.
SELECT * FROM Products;
Resultat:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Vi kan se att tabellen bara innehåller den ursprungliga raden.
Exempel 2 – Ignorera
Ett alternativ är att låta SQLite ignorera den felande raden. Med andra ord hoppar den över raden och fortsätter att bearbeta efterföljande rader.
För att göra detta inom din INSERT
uttalande, använd OR IGNORE
.
Effekten av detta är att INSERT
operationen lyckas, men utan några rader som bryter mot primärnyckelns begränsning.
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;
Resultat:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
I det här fallet försökte jag infoga två nya rader med ett ID som redan fanns i tabellen, så båda dessa rader hoppades över.
Exempel 3 – Ersätt
Ett annat alternativ du har är att ersätta den ursprungliga raden med den nya raden.
Med andra ord kommer du att skriva över befintliga data med dina nya data.
För att göra detta, använd 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;
Resultat:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
I det här fallet var de flesta rader desamma, så de innehåller samma data efter INSERT
drift. Vi kan dock se att den första raden har uppdaterats för att använda värdena i min INSERT
påstående.
Vi kan också se att den använde den andra uppsättningen värden (eftersom två delade samma ProductId ).
Så effekten är ungefär som en UPDATE
uttalande och INSERT
uttalande kombinerat.
Exempel 4 – Återställ
Ett annat alternativ är att använda ROLLBACK
alternativ.
Detta avbryter den aktuella SQL-satsen med ett SQLITE_CONSTRAINT-fel och återställer den aktuella transaktionen. Om ingen transaktion är aktiv (annat än den underförstådda transaktionen som skapas på varje kommando) fungerar den på samma sätt som ABORT
algoritm.
Det lönar sig att vara uppmärksam på hur detta alternativ fungerar. Här är ett exempel som använder flera INSERT OR ROLLBACK
uttalanden i en transaktion.
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;
Här är hela resultatet från min terminal när jag kör detta:
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>
Det som hände här är i princip att det har kommit så långt som till överträdelsen av begränsningen och sedan rullat tillbaka transaktionen. Sedan bearbetades de följande två raderna och sedan COMMIT
sökord påträffades. Då hade transaktionen redan återställts och så vi fick ett nytt felmeddelande om att ingen transaktion var aktiv.
Det här är vad som händer om jag tar bort det från transaktionen.
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;
Här är hela resultatet från min terminal när jag kör detta:
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>
I det här fallet fungerade det som ABORT
.
För att demonstrera detta, här är samma uttalande med ABORT
istället för 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;
Här är hela resultatet från min terminal när jag kör detta:
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>
Feilalternativet
FAIL
alternativet avbryter den aktuella SQL-satsen med ett SQLITE_CONSTRAINT-fel. Men det här alternativet tar inte tillbaka tidigare ändringar av SQL-satsen som misslyckades och avslutar inte heller transaktionen.
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;
Resultat:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5