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ą:
ABORTFAILIGNOREREPLACEROLLBACK
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
UNIQUElubPRIMARY KEYwystępuje naruszenie ograniczenia,REPLACEopcja 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 NULLwystępuje naruszenie ograniczenia, zastępujeNULLwartość z wartością domyślną dla tej kolumny lub jeśli kolumna nie ma wartości domyślnej, toABORTużywany jest algorytm. - Jeśli
CHECKwystępuje naruszenie ograniczenia lub klucza obcego, a następnieREPLACEdział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