SQLite má nestandardní klauzuli rozšíření SQL s názvem ON CONFLICT
což nám umožňuje specifikovat, jak se vypořádat s konflikty omezení.
Tato klauzule platí zejména pro UNIQUE
, NOT NULL
, CHECK
a PRIMARY KEY
omezení.
Tento článek poskytuje příklady toho, jak lze tuto klauzuli použít k určení, jak zpracovat konflikty omezení primárního klíče.
„Konflikty omezení primárního klíče“ mám na mysli, když se pokusíte vložit duplicitní hodnotu do sloupce primárního klíče. Ve výchozím nastavení, když se o to pokusíte, bude operace přerušena a SQLite vrátí chybu.
Ale můžete použít ON CONFLICT
klauzule ke změně způsobu, jakým SQLite řeší tyto situace.
Jednou z možností je použít tuto klauzuli v CREATE TABLE
při vytváření tabulky. Tím určíte, jak budou všechny INSERT
operace jsou ošetřeny.
Další možností je použít klauzuli na INSERT
kdykoli se pokusíte vložit data do tabulky. To vám umožní využít klauzuli, i když s ní nebyla vytvořena tabulka. Při použití této možnosti se syntaxe liší; používáte OR
místo ON CONFLICT
.
Příklady na této stránce využívají druhou možnost – vytvořím tabulku bez ON CONFLICT
klauzuli a místo toho uvedu OR
na INSERT
prohlášení.
Ukázková tabulka
Vytvoříme jednoduchou tabulku a přidáme jeden řádek.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Výsledek:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Aktuálně máme jeden řádek s ProductId z 1 .
Nyní můžeme projít různé scénáře vkládání dat do této tabulky, která porušuje omezení primárního klíče.
Příklad 1 – Přerušit (výchozí chování)
Jak již bylo zmíněno, výchozí chování pro SQLite je zrušit INSERT
operaci a vrátí chybu.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Výsledek:
Error: UNIQUE constraint failed: Products.ProductId
Byla vrácena chyba a nic nebylo vloženo.
Toto je ekvivalent použití OR ABORT
možnost.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Výsledek:
Error: UNIQUE constraint failed: Products.ProductId
Že nebylo nic vloženo, můžeme ověřit spuštěním SELECT
prohlášení proti tabulce.
SELECT * FROM Products;
Výsledek:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Vidíme, že tabulka obsahuje pouze původní řádek.
Příklad 2 – Ignorovat
Jednou z alternativ je nechat SQLite ignorovat problematický řádek. Jinými slovy, přeskočí řádek a bude pokračovat ve zpracování následujících řádků.
Chcete-li to provést v rámci INSERT
použijte příkaz OR IGNORE
.
Výsledkem je, že INSERT
operace je úspěšná, ale bez řádků, které porušují omezení primárního klíče.
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;
Výsledek:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
V tomto případě jsem se pokusil vložit dva nové řádky s ID, které již v tabulce existovalo, takže oba tyto řádky byly přeskočeny.
Příklad 3 – Nahradit
Další možností je nahradit původní řádek novým řádkem.
Jinými slovy, přepíšete stávající data svými novými daty.
Chcete-li to provést, použijte 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;
Výsledek:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
V tomto případě byla většina řádků stejná, takže za INSERT
obsahují stejná data úkon. Vidíme však, že první řádek byl aktualizován tak, aby používal hodnoty v mém INSERT
prohlášení.
Můžeme také vidět, že používá druhou sadu hodnot (přičemž dvě sdílejí stejné ProductId ).
Takže efekt je něco jako UPDATE
a INSERT
prohlášení kombinované.
Příklad 4 – Vrácení zpět
Další možností je použít ROLLBACK
možnost.
To zruší aktuální příkaz SQL s chybou SQLITE_CONSTRAINT a vrátí aktuální transakci zpět. Pokud není aktivní žádná transakce (kromě implikované transakce, která je vytvořena při každém příkazu), funguje stejně jako ABORT
algoritmu.
Vyplatí se mít na paměti, jak tato možnost funguje. Zde je příklad, který používá více INSERT OR ROLLBACK
výpisy v rámci transakce.
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;
Zde je úplný výstup z mého terminálu, když to spustím:
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>
V podstatě to, co se stalo, je to, že se to dostalo až k porušení omezení a pak transakci odvolala. Poté byly zpracovány další dva řádky a poté COMMIT
bylo nalezeno klíčové slovo. V té době již byla transakce vrácena zpět, a tak jsme dostali další chybu, která nám sdělovala, že žádná transakce nebyla aktivní.
Zde je to, co se stane, když jej odstraním z transakce.
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;
Zde je úplný výstup z mého terminálu, když to spustím:
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>
V tomto případě to fungovalo jako ABORT
.
Abychom to demonstrovali, zde je stejné prohlášení pomocí ABORT
místo 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;
Zde je úplný výstup z mého terminálu, když to spustím:
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>
Možnost selhání
FAIL
volba zruší aktuální příkaz SQL s chybou SQLITE_CONSTRAINT. Tato možnost však neodvolá předchozí změny příkazu SQL, které selhaly, ani neukončí transakci.
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;
Výsledek:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5