SQLite har ON CONFLICT
klausul som låter dig specificera hur begränsningskonflikter ska hanteras. Det gäller UNIQUE
, NOT NULL
, CHECK
och PRIMARY KEY
begränsningar (men inte FOREIGN KEY
begränsningar).
Det finns fem möjliga alternativ du kan använda med denna sats:
ABORT
FAIL
IGNORE
REPLACE
ROLLBACK
Den här artikeln ger exempel och en förklaring av vart och ett av dessa alternativ.
ON CONFLICT
sats används i CREATE TABLE
satser, men det kan också användas när du infogar eller uppdaterar data genom att ersätta ON CONFLICT
med OR
.
När du skapar tabellen
Som nämnts kan du använda ON CONFLICT
när du skapar tabellen eller när du infogar/uppdaterar data.
Här är ett exempel på hur du använder ON CONFLICT
vid tidpunkten för att skapa tabellen.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL ON CONFLICT IGNORE,
Price
);
När du använder ON CONFLICT
klausul tillämpar du den på den specifika begränsningen som du vill hantera. I det här fallet lade jag till satsen till en NOT NULL
begränsning.
I det här fallet angav jag IGNORE
, vilket innebär att om det finns en begränsningsöverträdelse kommer SQLite att hoppa över den raden och sedan fortsätta bearbetningen.
Om jag nu försöker infoga NULL
i Produktnamn kolumnen den raden hoppas över.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120.0
När du infogar data
Du kan också använda den här klausulen när du infogar och uppdaterar data. Skillnaden är att du byter ut ON CONFLICT
med OR
.
För att demonstrera släpper jag den föregående tabellen och skapar den igen, men utan ON CONFLICT
klausul:
DROP TABLE IF EXISTS Products;
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL,
Price
);
Nu ska jag infoga samma data och använda OR IGNORE
för att hoppa över raden som bryter mot begränsningen.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120.0
Så vi får samma resultat som i föregående exempel.
I dessa exempel använde jag IGNORE
alternativ. Detta är bara ett av fem möjliga alternativ för denna klausul.
Nedan finns exempel med vart och ett av de fem alternativen.
Avbryt
Detta alternativ avbryter den aktuella SQL-satsen med ett SQLITE_CONSTRAINT-fel och backar ut alla ändringar som gjorts av den aktuella SQL-satsen; men ändringar orsakade av tidigare SQL-satser inom samma transaktion bevaras och transaktionen förblir aktiv.
Detta är standardbeteendet. Med andra ord, detta är vad som händer vid överträdelser av begränsningar när du inte använder ON CONFLICT
klausul.
Här är ett exempel på vad som händer när du anger 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;
Resultat:
Inga resultat returnerades eftersom INSERT
operationen avbröts och tabellen är därför tom.
Här är vad som händer om jag lägger varje rad i sin egen INSERT
uttalande i en transaktion.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120.0
Fel
FAIL
alternativet avbryter den aktuella SQL-satsen med ett SQLITE_CONSTRAINT-fel. Men den backar inte tillbaka tidigare ändringar av SQL-satsen som misslyckades och avslutar inte heller transaktionen.
Här är ett exempel.
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;
Resultat:
ProductId ProductName Pris ---------- ---------- ----------1 Hammer 9,99
Här är den in med separat INSERT
uttalanden i en transaktion.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120.0
Ignorera
IGNORE
alternativet hoppar över den ena raden som innehåller begränsningsöverträdelsen och fortsätter att bearbeta efterföljande rader i SQL-satsen som om inget gick fel. Andra rader före och efter raden som innehöll begränsningsöverträdelsen infogas eller uppdateras normalt. Inget fel returneras för unikhet, NOT NULL
och UNIQUE
begränsningsfel när detta alternativ används. Det här alternativet fungerar dock som ABORT
för främmande nyckelbegränsningsfel.
De första exemplen på den här sidan använder IGNORE
, men här är den igen.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120.0
Ersätt
REPLACE
alternativet fungerar olika beroende på överträdelsen:
- När en
UNIQUE
ellerPRIMARY KEY
begränsningsöverträdelse inträffar,,REPLACE
alternativet tar bort redan existerande rader som orsakar begränsningsöverträdelsen innan den aktuella raden infogas eller uppdateras och kommandot fortsätter att köras normalt. - Om en
NOT NULL
begränsningsöverträdelse inträffar, den ersätterNULL
värde med standardvärdet för den kolumnen, eller om kolumnen inte har något standardvärde, dåABORT
algoritm används. - Om en
CHECK
begränsning eller främmande nyckel begränsning sker, sedanREPLACE
fungerar somABORT
.
Om den tar bort rader för att uppfylla en begränsning, utlöses utlösare om och endast om rekursiva utlösare är aktiverade.
Här är ett exempel som använder REPLACE
alternativ.
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;
Resultat:
ProductId ProductName Pris ---------- ------------------ ----------1 Skiftnyckel 37,0 2 Spikar 1,49 3 Såg 11,34 5 Mejsel 23,0 6 Bandage 120.0
I det här exemplet var konflikten med primärnyckeln (jag försökte infoga två rader med samma ProductId ). REPLACE
alternativet fick den andra att ersätta den första.
Återställ
Ett annat alternativ är att använda ROLLBACK
.
Det här alternativet 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.
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', 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;
Här är hela resultatet från min terminal när jag kör detta:
sqlite> DELETE FROM Products;sqlite> sqlite> BÖRJA TRANSAKTION;sqlite> INFOGA ELLER ÅTERBAKA IN I produkter VÄRDEN (1, 'Hammer', 9,99);sqlite> INFOGA ELLER ÅTERVÄNDA INTO Products VALUES, (1,499); Fel:NOT NULL-begränsning misslyckades:Products.ProductNamesqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00 INSERTRO INSERTROe> INTO Products VALUES (5, 'Mejsel', 23.00);sqlite> INFOGA ELLER ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00);sqlite> COMMIT;Fel:kan inte begå - ingen transaktion är aktivsqlite> sqlite> SELECT Produkter;ProductId Produktnamn Pris ---------- ------------------ ----------3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120,0Så det kom till begränsningsöverträdelsen och återställde sedan transaktionen. Sedan bearbetades de efterföljande 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', 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;
Här är hela resultatet från min terminal när jag kör detta:
sqlite> DELETE FROM Products;sqlite> sqlite> INFOGA ELLER ÅTERLÄMNA I PRODUKTERVÄRDEN (1, 'Hammer', 9,99);sqlite> INFOGA ELLER ÅTERBAKA INTO PRODUKTERVÄRDEN (2, NULL, 1,49); fel:int:misslyckades:Products.ProductNamesqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);sqlite> INSERT ELLER ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00);sqlite> INSERT INTO ROLLs , 'Mejsel', 23.00);sqlite> INFOGA ELLER ÅTERBAKA IN I Products VALUES (6, 'Bandage', 120.00);sqlite> sqlite> VÄLJ * FRÅN Produkter;ProductId Produktnamn Pris ---------- -- ---------- ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120,0I det här fallet fungerade det som
ABORT
.För att bekräfta, här är samma uttalande med
ABORT
istället förROLLBACK
.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;
Här är hela resultatet från min terminal när jag kör detta:
sqlite> DELETE FROM Products;sqlite> sqlite> INFOGA ELLER AVBRYT I PRODUKTERVÄRDEN (1, 'Hammer', 9,99);sqlite> INFOGA ELLER AVBRYTA I PRODUKTERVÄRDEN (2, NULL, 1,49);Fel:INTE NULL-begränsning misslyckades: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 (5 Products VALUES) , 'Mejsel', 23.00);sqlite> INFOGA ELLER AVBRYT I PRODUKTS VÄRDEN (6, 'Bandage', 120.00);sqlite> sqlite> VÄLJ * FRA produkter;ProductId Produktnamn Pris ---------- -- ---------- ----------1 Hammare 9,99 3 Såg 11,34 4 Skiftnyckel 37,0 5 Mejsel 23,0 6 Bandage 120,0