SQLite има ON CONFLICT клауза, която ви позволява да укажете как да се справите с конфликти на ограничения. Прилага се за UNIQUE , NOT NULL , CHECK и PRIMARY KEY ограничения (но не и FOREIGN KEY ограничения).
Има пет възможни опции, които можете да използвате с тази клауза:
ABORTFAILIGNOREREPLACEROLLBACK
Тази статия предоставя примери и обяснение на всяка от тези опции.
ON CONFLICT клаузата се използва в CREATE TABLE изрази, но може да се използва и при вмъкване или актуализиране на данни чрез замяна на ON CONFLICT с OR .
При създаване на таблицата
Както споменахме, можете да използвате ON CONFLICT когато създавате таблицата или когато вмъквате/актуализирате данни.
Ето пример за използване на ON CONFLICT в момента на създаване на таблицата.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL ON CONFLICT IGNORE,
Price
);
Когато използвате ON CONFLICT клауза, я прилагате към специфичното ограничение, което искате да обработвате. В този случай добавих клаузата към NOT NULL ограничение.
В този случай посочих IGNORE , което означава, че ако има нарушение на ограничението, SQLite ще пропусне този ред и след това ще продължи обработката.
Сега, ако се опитам да вмъкна NULL в ProductName колона този ред е пропуснат.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120.0
При вмъкване на данни
Можете също да използвате тази клауза, когато вмъквате и актуализирате данни. Разликата е, че замествате ON CONFLICT с OR .
За да демонстрирам, ще пусна предишната таблица и ще я създам отново, но без ON CONFLICT клауза:
DROP TABLE IF EXISTS Products;
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL,
Price
);
Сега ще вмъкна същите данни и ще използвам OR IGNORE за да пропуснете реда, който нарушава ограничението.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120.0
Така получаваме същия резултат като в предишния пример.
В тези примери използвах IGNORE опция. Това е само една от петте възможни опции за тази клауза.
По-долу са дадени примери, използващи всяка от петте опции.
Прекратяване
Тази опция прекратява текущия SQL оператор с грешка SQLITE_CONSTRAINT и отменя всички промени, направени от текущия SQL оператор; но промените, причинени от предишни SQL оператори в рамките на същата транзакция, се запазват и транзакцията остава активна.
Това е поведението по подразбиране. С други думи, това се случва по време на нарушения на ограниченията, когато не използвате ON CONFLICT клауза.
Ето пример за това какво се случва, когато посочите 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; Резултат:
Не бяха върнати резултати, защото INSERT операцията е прекратена и таблицата е празна.
Ето какво се случва, ако поставя всеки ред в собствен INSERT извлечение в рамките на транзакция.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120.0
Неуспешно
FAIL опция прекратява текущия SQL израз с грешка SQLITE_CONSTRAINT. Но той не отменя предишни промени на SQL израза, който не е успял, нито прекратява транзакцията.
Ето един пример.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Hammer 9,99
Ето го с отделен INSERT извлечения в рамките на транзакция.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120.0
Игнориране
IGNORE опцията пропуска единия ред, който съдържа нарушението на ограничението и продължава да обработва следващите редове от SQL израза, сякаш нищо не се е объркало. Други редове преди и след реда, съдържащ нарушението на ограничението, се вмъкват или актуализират нормално. Не се връща грешка за уникалност, NOT NULL и UNIQUE грешки при ограничаване, когато се използва тази опция. Тази опция обаче работи като ABORT за грешки при ограничаване на външния ключ.
Първите примери на тази страница използват IGNORE , но ето го отново.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120.0
Замяна
REPLACE опцията работи различно в зависимост от нарушението:
- Когато е
UNIQUEилиPRIMARY KEYвъзниква нарушение на ограничението,REPLACEопцията изтрива вече съществуващи редове, които причиняват нарушение на ограничението, преди да вмъкне или актуализира текущия ред и командата продължава да се изпълнява нормално. - Ако
NOT NULLвъзниква нарушение на ограничението, то заменяNULLстойност със стойността по подразбиране за тази колона или ако колоната няма стойност по подразбиране, тогаваABORTсе използва алгоритъм. - Ако
CHECKвъзникне нарушение на ограничение или ограничение на външния ключ, след коетоREPLACEработи катоABORT.
Освен това, ако изтрие редове, за да удовлетвори ограничение, тригерите за изтриване се задействат, ако и само ако рекурсивните задействания са разрешени.
Ето пример, който използва REPLACE опция.
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; Резултат:
ProductId ProductName Цена ---------- ----------- ----------1 Гаечен ключ 37,0 2 Пирон 1,49 3 Трион 11,34 5 Длето 23,0 6 Бинт 120.0
В този пример конфликтът беше с първичния ключ (опитах се да вмъкна два реда със същия ProductId ). REPLACE опция накара втория да замени първия.
Отмяна
Друга възможност е да използвате ROLLBACK .
Тази опция прекратява текущия SQL оператор с грешка SQLITE_CONSTRAINT и връща текущата транзакция. Ако нито една транзакция не е активна (освен подразбиращата се транзакция, която се създава при всяка команда), тя работи по същия начин като ABORT алгоритъм.
Ето пример, който използва множество INSERT OR ROLLBACK извлечения в рамките на транзакция.
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; Ето пълния изход от моя терминал, когато стартирам това:
sqlite> ИЗТРИВАНЕ ОТ продукти;sqlite> sqlite> ЗАПОЧНЕТЕ ТРАНЗАКЦИЯ;sqlite> ВМЪКВАНЕ ИЛИ ВРЪЩАНЕ В ПРОДУКТИ СТОЙНОСТИ (1, 'Hammer', 9.99);sqlite> ВМЪКВАНЕ ИЛИ връщане в стойности на продукти (2.49); Грешка:Ограничението NOT NULL неуспешно:Products.ProductNamesqlite> INSERT OR ROLLBACK IN TO Products VALUES (3, 'Saw', 11.34);sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00 ROLLBACK> IN); INTO СТОЙНОСТИ (5, 'Chisel', 23.00);sqlite> ВМЕСТЕ ИЛИ ВРЪЩАНЕ В ПРОДУКТИ СТОЙНОСТИ (6, 'Bandage', 120.00);sqlite> COMMIT; Грешка:не може да се извърши - няма активна транзакция sqlite> * FROM> SELECT Продукти;ProductId ProductName Цена ---------- ----------- ----------3 Трион 11,34 4 Гаечен ключ 37,0 5 Длетото 23,0 6 Бинт 120,0 предварително>Така се стигна до нарушението на ограничението, след което върна транзакцията. След това бяха обработени следващите редове и след това
COMMITбеше открита ключова дума. Дотогава транзакцията вече беше отменена и така получихме друга грешка, която ни казва, че няма активна транзакция.Ето какво се случва, ако го премахна от транзакцията.
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;Ето пълния изход от моя терминал, когато стартирам това:
sqlite> ИЗТРИВАНЕ ОТ продукти;sqlite> sqlite> ВМЪКВАНЕ ИЛИ ВРЪЩАНЕ В ПРОДУКТИ СТОЙНОСТИ (1, 'Hammer', 9.99);sqlite> ВМЕСВАНЕ ИЛИ ВРЪЩАНЕ В ПРОДУКТИ СТОЙНОСТИ (2, NULL, 1.49); Грешка constra:NUINT неуспешно:Products.ProductNamesqlite> INSERT OR ROLLBACK IN TO Products STOS (3, 'Saw', 11.34);sqlite> INSERT OR ROLLBACK IN TO Products STOS (4, 'Wrench', 37.00);sqlite> INSERT STO (ВМЪКВАНЕ НА СТОЙНОСТ ИЛИ 5 Product ROLLB) , 'Chisel', 23.00);sqlite> ВМЕСТЕ ИЛИ ВРЪЩАНЕ В ПРОДУКТИ СТОЙНОСТИ (6, 'Bandage', 120.00);sqlite> sqlite> ИЗБЕРЕТЕ * ОТ Продукти;ProductId ProductName Цена ---------- -- --------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120,0В този случай работеше като
ABORT.За да потвърдите, ето същото изявление с помощта на
ABORTвместо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;Ето пълния изход от моя терминал, когато стартирам това:
sqlite> ИЗТРИВАНЕ ОТ продукти;sqlite> sqlite> ВМЪКВАНЕ ИЛИ ПРЕКРАТЯВАНЕ В ПРОДУКТИ СТОЙНОСТИ (1, 'Hammer', 9.99);sqlite> ВМЕСВАНЕ ИЛИ ПРЕКРАТЯВАНЕ В ПРОДУКТИ СТОЙНОСТИ (2, NULL, 1.49); Грешка constraint NULL неуспешно: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 ABORS VALUE IN (5 , 'Chisel', 23.00);sqlite> ВМЕСТЕ ИЛИ ПРЕКРАНЕТЕ В ПРОДУКТИ СТОЙНОСТИ (6, 'Bandage', 120.00);sqlite> sqlite> ИЗБЕРЕТЕ * ОТ Продукти;ProductId ProductName Цена ---------- -- --------- ----------1 Чук 9,99 3 Трион 11,34 4 Гаечен ключ 37,0 5 Длето 23,0 6 Бинт 120,0