SQLite ma ON CONFLICT
klauzula, która pozwala określić sposób obsługi konfliktów ograniczeń. Dotyczy UNIQUE
, NOT NULL
, CHECK
i PRIMARY KEY
ograniczenia (ale nie FOREIGN KEY
ograniczenia).
Istnieje pięć możliwych opcji, których można użyć z tą klauzulą:
ABORT
FAIL
IGNORE
REPLACE
ROLLBACK
Ten artykuł zawiera przykłady i wyjaśnienie każdej z tych opcji.
ON CONFLICT
klauzula jest używana w CREATE TABLE
oświadczenia, ale można go również użyć podczas wstawiania lub aktualizowania danych, zastępując ON CONFLICT
z OR
.
Podczas tworzenia tabeli
Jak wspomniano, możesz użyć ON CONFLICT
podczas tworzenia tabeli lub podczas wstawiania/aktualizowania danych.
Oto przykład użycia ON CONFLICT
w momencie tworzenia tabeli.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL ON CONFLICT IGNORE,
Price
);
Gdy używasz ON CONFLICT
klauzulę, zastosujesz ją do konkretnego ograniczenia, które chcesz obsłużyć. W tym przypadku dodałem klauzulę do NOT NULL
ograniczenie.
W tym przypadku określiłem IGNORE
, co oznacza, że jeśli wystąpi naruszenie ograniczenia, SQLite pominie ten wiersz, a następnie będzie kontynuować przetwarzanie.
Teraz, jeśli spróbuję wstawić NULL
do ProductName kolumna, której wiersz jest pomijany.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Podczas wstawiania danych
Możesz również użyć tej klauzuli podczas wstawiania i aktualizowania danych. Różnica polega na tym, że zastępujesz ON CONFLICT
z OR
.
Aby to zademonstrować, usunę poprzednią tabelę i utworzę ją ponownie, ale bez ON CONFLICT
klauzula:
DROP TABLE IF EXISTS Products;
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL,
Price
);
Teraz wstawię te same dane i użyję OR IGNORE
aby pominąć wiersz, który narusza ograniczenie.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Otrzymujemy więc ten sam wynik, co w poprzednim przykładzie.
W tych przykładach użyłem IGNORE
opcja. To tylko jedna z pięciu możliwych opcji dla tej klauzuli.
Poniżej znajdują się przykłady użycia każdej z pięciu opcji.
Przerwij
Ta opcja przerywa bieżącą instrukcję SQL z błędem SQLITE_CONSTRAINT i wycofuje wszelkie zmiany wprowadzone przez bieżącą instrukcję SQL; ale zmiany spowodowane wcześniejszymi instrukcjami SQL w ramach tej samej transakcji są zachowywane, a transakcja pozostaje aktywna.
To jest zachowanie domyślne. Innymi słowy, dzieje się tak podczas naruszeń ograniczeń, gdy nie używasz ON CONFLICT
klauzula.
Oto przykład tego, co się dzieje, gdy podasz 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;
Wynik:
Nie zwrócono żadnych wyników, ponieważ INSERT
operacja została przerwana i dlatego tabela jest pusta.
Oto, co się stanie, jeśli umieszczę każdy wiersz we własnym INSERT
wyciąg w ramach transakcji.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Niepowodzenie
FAIL
opcja przerywa bieżącą instrukcję SQL z błędem SQLITE_CONSTRAINT. Ale nie wycofuje wcześniejszych zmian w instrukcji SQL, która się nie powiodła, ani nie kończy transakcji.
Oto przykład.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99
Tutaj jest w oddzielnym INSERT
wyciągi w ramach transakcji.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Ignoruj
IGNORE
opcja pomija jeden wiersz, który zawiera naruszenie ograniczenia i kontynuuje przetwarzanie kolejnych wierszy instrukcji SQL, tak jakby nic nie poszło nie tak. Inne wiersze przed i za wierszem zawierającym naruszenie ograniczenia są wstawiane lub aktualizowane normalnie. Żaden błąd nie jest zwracany dla wyjątkowości, NOT NULL
i UNIQUE
błędy wiązań, gdy ta opcja jest używana. Jednak ta opcja działa jak ABORT
dla błędów ograniczeń klucza obcego.
Pierwsze przykłady na tej stronie używają IGNORE
, ale znowu jest.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Zamień
REPLACE
opcja działa różnie w zależności od naruszenia:
- Gdy
UNIQUE
lubPRIMARY KEY
występuje naruszenie ograniczenia,REPLACE
opcja usuwa wcześniej istniejące wiersze, które powodują naruszenie ograniczenia przed wstawieniem lub aktualizacją bieżącego wiersza, a polecenie kontynuuje normalne wykonywanie. - Jeśli
NOT NULL
występuje naruszenie ograniczenia, zastępujeNULL
wartość z wartością domyślną dla tej kolumny lub jeśli kolumna nie ma wartości domyślnej, toABORT
używany jest algorytm. - Jeśli
CHECK
występuje naruszenie ograniczenia lub klucza obcego, a następnieREPLACE
działa jakABORT
.
Ponadto, jeśli usuwa wiersze w celu spełnienia ograniczenia, wyzwalacze usuwania są uruchamiane wtedy i tylko wtedy, gdy włączone są wyzwalacze rekurencyjne.
Oto przykład, w którym użyto REPLACE
opcja.
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;
Wynik:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 37.0 2 Nails 1.49 3 Saw 11.34 5 Chisel 23.0 6 Bandage 120.0
W tym przykładzie konflikt dotyczył klucza podstawowego (próbowałem wstawić dwa wiersze z tym samym ProductId ). REPLACE
opcja spowodowała, że druga zastąpiła pierwszą.
Wycofanie
Inną opcją jest użycie ROLLBACK
.
Ta opcja przerywa bieżącą instrukcję SQL z błędem SQLITE_CONSTRAINT i wycofuje bieżącą transakcję. Jeśli żadna transakcja nie jest aktywna (inna niż dorozumiana transakcja, która jest tworzona przy każdym poleceniu), to działa tak samo jak ABORT
algorytm.
Oto przykład, który używa wielu INSERT OR ROLLBACK
wyciągi w ramach transakcji.
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;
Oto pełne dane wyjściowe z mojego terminala po uruchomieniu tego:
sqlite> DELETE FROM Products; sqlite> sqlite> BEGIN TRANSACTION; sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); 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 ---------- ----------- ---------- 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Doszło więc do naruszenia ograniczenia, a następnie wycofało transakcję. Następnie kolejne wiersze zostały przetworzone, a następnie COMMIT
napotkano słowo kluczowe. Do tego czasu transakcja została już wycofana, więc pojawił się kolejny błąd informujący nas, że żadna transakcja nie była aktywna.
Oto, co się stanie, jeśli usunę go z transakcji.
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;
Oto pełne dane wyjściowe z mojego terminala po uruchomieniu tego:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); 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 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
W tym przypadku zadziałało to jak ABORT
.
Aby potwierdzić, oto to samo oświadczenie przy użyciu ABORT
zamiast ROLLBACK
.
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;
Oto pełne dane wyjściowe z mojego terminala po uruchomieniu tego:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> 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 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 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0