SQLite има нестандартна клауза за разширение на SQL, наречена ON CONFLICT
което ни позволява да уточним как да се справяме с конфликтите на ограничения.
По-специално, клаузата се отнася за UNIQUE
, NOT NULL
, CHECK
и PRIMARY KEY
ограничения.
Тази статия предоставя примери за това как тази клауза може да се използва, за да се определи как да се обработват конфликти на ограничения на първичния ключ.
Под „конфликти на ограничения на първичния ключ“ имам предвид, когато се опитвате да вмъкнете дублирана стойност в колона за първичен ключ. По подразбиране, когато се опитате да направите това, операцията ще бъде прекъсната и SQLite ще върне грешка.
Но можете да използвате ON CONFLICT
клауза за промяна на начина, по който SQLite се справя с тези ситуации.
Една от опциите е да използвате тази клауза в CREATE TABLE
изявление при създаване на таблицата. Това ще определи как всички INSERT
операции се лекуват.
Друга възможност е да използвате клаузата в INSERT
изявление всеки път, когато се опитате да вмъкнете данни в таблицата. Това ви позволява да се възползвате от клаузата, дори когато таблицата не е създадена с нея. Когато използвате тази опция, синтаксисът е различен; използвате OR
вместо ON CONFLICT
.
Примерите на тази страница използват втората опция – създавам таблицата без ON CONFLICT
клауза и вместо това указвам OR
на INSERT
изявление.
Примерна таблица
Нека създадем проста таблица и да добавим един ред.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Резултат:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
В момента имаме един ред с ProductId от 1 .
Сега можем да преминем през различните сценарии на вмъкване на данни в тази таблица, която нарушава ограничението на първичния ключ.
Пример 1 – Прекратяване (поведение по подразбиране)
Както споменахме, поведението по подразбиране за SQLite е да прекъсне INSERT
операция и върне грешка.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Резултат:
Error: UNIQUE constraint failed: Products.ProductId
Беше върната грешка и нищо не беше въведено.
Това е еквивалентно на използването на OR ABORT
опция.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Резултат:
Error: UNIQUE constraint failed: Products.ProductId
Можем да проверим дали нищо не е вмъкнато, като изпълним SELECT
изявление срещу масата.
SELECT * FROM Products;
Резултат:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Можем да видим, че таблицата съдържа само оригиналния ред.
Пример 2 – Игнориране
Една алтернатива е SQLite да игнорира нарушителния ред. С други думи, той ще пропусне реда и ще продължи да обработва следващите редове.
За да направите това във вашия INSERT
израз, използвайте OR IGNORE
.
Ефектът от това е, че INSERT
операцията е успешна, но без редове, които нарушават ограничението на първичния ключ.
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;
Резултат:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
В този случай се опитах да вмъкна два нови реда с идентификатор, който вече съществуваше в таблицата, така че и двата реда бяха пропуснати.
Пример 3 – Замяна
Друга опция, която имате, е да замените оригиналния ред с новия.
С други думи, ще презапишете съществуващите данни с новите си данни.
За да направите това, използвайте 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;
Резултат:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
В този случай повечето редове бяха еднакви, така че съдържат едни и същи данни след INSERT
операция. Въпреки това можем да видим, че първият ред е актуализиран, за да използва стойностите в моя INSERT
изявление.
Можем също да видим, че е използвал втория набор от стойности (виждайки, че двама споделят един и същ ProductId ).
Така че ефектът е нещо като UPDATE
изявление и INSERT
комбинирано изявление.
Пример 4 – връщане назад
Друга възможност е да използвате ROLLBACK
опция.
Това прекратява текущия SQL оператор с грешка SQLITE_CONSTRAINT и връща текущата транзакция. Ако нито една транзакция не е активна (освен подразбиращата се транзакция, която се създава при всяка команда), тя работи по същия начин като ABORT
алгоритъм.
Струва си да имате предвид как работи тази опция. Ето пример, който използва множество INSERT OR ROLLBACK
извлечения в рамките на транзакция.
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;
Ето пълния изход от моя терминал, когато стартирам това:
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>
По същество това, което се случи тук, е, че е стигнало до нарушението на ограничението, след което е върнало транзакцията. След това бяха обработени следващите два реда и след това COMMIT
беше открита ключова дума. Дотогава транзакцията вече беше отменена и така получихме друга грешка, която ни казва, че няма активна транзакция.
Ето какво се случва, ако го премахна от транзакцията.
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;
Ето пълния изход от моя терминал, когато стартирам това:
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>
В този случай работеше като ABORT
.
За да демонстрирате това, ето същото изявление с помощта на ABORT
вместо 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;
Ето пълния изход от моя терминал, когато стартирам това:
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>
Опцията за отказ
FAIL
опция прекратява текущия SQL израз с грешка SQLITE_CONSTRAINT. Но тази опция не отменя предишни промени на SQL израза, който не е успял, нито прекратява транзакцията.
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;
Резултат:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5