SQLite има ON CONFLICT
клауза, която ви позволява да укажете как да се справите с конфликти на ограничения. Прилага се за UNIQUE
, NOT NULL
, CHECK
и PRIMARY KEY
ограничения (но не и FOREIGN KEY
ограничения).
Има пет възможни опции, които можете да използвате с тази клауза:
ABORT
FAIL
IGNORE
REPLACE
ROLLBACK
Тази статия предоставя примери и обяснение на всяка от тези опции.
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