SQLite có một mệnh đề mở rộng SQL không chuẩn được gọi là ON CONFLICT
cho phép chúng tôi chỉ định cách giải quyết các xung đột ràng buộc.
Đặc biệt, điều khoản áp dụng cho UNIQUE
, NOT NULL
, CHECK
và PRIMARY KEY
ràng buộc.
Bài viết này cung cấp các ví dụ về cách mệnh đề này có thể được sử dụng để xác định cách xử lý xung đột ràng buộc khóa chính.
"Xung đột ràng buộc khóa chính", ý tôi là khi bạn cố gắng chèn một giá trị trùng lặp vào cột khóa chính. Theo mặc định, khi bạn cố gắng thực hiện việc này, hoạt động sẽ bị hủy bỏ và SQLite sẽ trả về lỗi.
Nhưng bạn có thể sử dụng ON CONFLICT
để thay đổi cách SQLite giải quyết những tình huống này.
Một tùy chọn là sử dụng mệnh đề này trong CREATE TABLE
khi tạo bảng. Làm điều đó sẽ xác định cách tất cả INSERT
các hoạt động được xử lý.
Một tùy chọn khác là sử dụng mệnh đề trên INSERT
câu lệnh bất cứ khi nào bạn cố gắng chèn dữ liệu vào bảng. Điều này cho phép bạn tận dụng điều khoản ngay cả khi bảng không được tạo với nó. Khi bạn sử dụng tùy chọn này, cú pháp sẽ khác; bạn sử dụng OR
thay vì ON CONFLICT
.
Các ví dụ trên trang này sử dụng tùy chọn thứ hai - Tôi tạo bảng không có ON CONFLICT
và thay vào đó tôi chỉ định OR
trên INSERT
tuyên bố.
Bảng mẫu
Hãy tạo một bảng đơn giản và thêm một hàng.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Kết quả:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Chúng tôi hiện có một hàng, với ProductId trong tổng số 1 .
Bây giờ chúng ta có thể chạy qua các tình huống khác nhau của việc chèn dữ liệu vào bảng đó vi phạm giới hạn khóa chính.
Ví dụ 1 - Hủy bỏ (Hành vi mặc định)
Như đã đề cập, hành vi mặc định cho SQLite là hủy bỏ INSERT
hoạt động và trả về lỗi.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Kết quả:
Error: UNIQUE constraint failed: Products.ProductId
Một lỗi đã được trả lại và không có gì được chèn vào.
Điều này tương đương với việc sử dụng OR ABORT
tùy chọn.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Kết quả:
Error: UNIQUE constraint failed: Products.ProductId
Chúng tôi có thể xác minh rằng không có gì được chèn bằng cách chạy SELECT
tuyên bố chống lại bảng.
SELECT * FROM Products;
Kết quả:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Chúng ta có thể thấy rằng bảng chỉ chứa hàng gốc.
Ví dụ 2 - Bỏ qua
Một thay thế là để SQLite bỏ qua hàng vi phạm. Nói cách khác, nó sẽ bỏ qua hàng và tiếp tục xử lý các hàng tiếp theo.
Để thực hiện việc này trong INSERT
của bạn tuyên bố, sử dụng OR IGNORE
.
Tác dụng của việc này là INSERT
thao tác thành công nhưng không có bất kỳ hàng nào vi phạm giới hạn khóa chính.
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;
Kết quả:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
Trong trường hợp này, tôi đã cố gắng chèn hai hàng mới với một ID đã tồn tại trong bảng, vì vậy cả hai hàng đó đều bị bỏ qua.
Ví dụ 3 - Thay thế
Một tùy chọn khác mà bạn có là thay thế hàng ban đầu bằng hàng mới.
Nói cách khác, bạn sẽ ghi đè dữ liệu hiện có bằng dữ liệu mới của mình.
Để thực hiện việc này, hãy sử dụng 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;
Kết quả:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
Trong trường hợp này, hầu hết các hàng đều giống nhau, vì vậy chúng chứa cùng một dữ liệu sau INSERT
hoạt động. Tuy nhiên, chúng ta có thể thấy rằng hàng đầu tiên đã được cập nhật để sử dụng các giá trị trong INSERT
của tôi tuyên bố.
Chúng tôi cũng có thể thấy rằng nó đã sử dụng bộ giá trị thứ hai (coi như hai giá trị được chia sẻ cùng một ProductId ).
Vì vậy, hiệu ứng giống như một UPDATE
câu lệnh và INSERT
tuyên bố kết hợp.
Ví dụ 4 - Khôi phục
Một tùy chọn khác là sử dụng ROLLBACK
tùy chọn.
Thao tác này hủy bỏ câu lệnh SQL hiện tại với lỗi SQLITE_CONSTRAINT và khôi phục giao dịch hiện tại. Nếu không có giao dịch nào đang hoạt động (ngoài giao dịch ngụ ý được tạo trên mọi lệnh) thì giao dịch đó hoạt động giống như ABORT
thuật toán.
Cần lưu ý đến cách thức hoạt động của tùy chọn này. Dưới đây là một ví dụ sử dụng nhiều INSERT OR ROLLBACK
báo cáo trong một giao dịch.
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;
Đây là kết quả đầu ra đầy đủ từ thiết bị đầu cuối của tôi khi tôi chạy điều này:
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>
Về cơ bản những gì đã xảy ra ở đây là, nó đã đi đến mức vi phạm ràng buộc, sau đó quay trở lại giao dịch. Sau đó, hai dòng tiếp theo được xử lý và sau đó là COMMIT
từ khóa đã gặp phải. Đến lúc đó, giao dịch đã được khôi phục và do đó, chúng tôi gặp một lỗi khác cho chúng tôi biết rằng không có giao dịch nào đang hoạt động.
Đây là những gì sẽ xảy ra nếu tôi xóa nó khỏi giao dịch.
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;
Đây là kết quả đầu ra đầy đủ từ thiết bị đầu cuối của tôi khi tôi chạy điều này:
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>
Trong trường hợp này, nó hoạt động giống như ABORT
.
Để chứng minh điều này, đây là câu lệnh tương tự sử dụng ABORT
thay vì 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;
Đây là kết quả đầu ra đầy đủ từ thiết bị đầu cuối của tôi khi tôi chạy điều này:
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>
Tùy chọn thất bại
FAIL
tùy chọn hủy bỏ câu lệnh SQL hiện tại với lỗi SQLITE_CONSTRAINT. Nhưng tùy chọn này không hỗ trợ các thay đổi trước đó của câu lệnh SQL không thành công cũng như không kết thúc giao dịch.
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;
Kết quả:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5