Q&A Style
Vâng, sau khi nghiên cứu và đấu tranh với vấn đề trong nhiều giờ, tôi phát hiện ra rằng có hai cách để thực hiện điều này, tùy thuộc vào cấu trúc bảng của bạn và nếu bạn đã kích hoạt các hạn chế về khóa ngoại để duy trì tính toàn vẹn. Tôi muốn chia sẻ điều này ở định dạng rõ ràng để tiết kiệm thời gian cho những người có thể ở trong hoàn cảnh của tôi.
Tùy chọn 1: Bạn có thể đủ khả năng xóa hàng
Nói cách khác, bạn không có khóa ngoại hoặc nếu bạn có khóa ngoại, công cụ SQLite của bạn được định cấu hình để không có ngoại lệ toàn vẹn. Cách để đi là CHÈN HOẶC THAY THẾ . Nếu bạn đang cố gắng chèn / cập nhật trình phát có ID đã tồn tại, công cụ SQLite sẽ xóa hàng đó và chèn dữ liệu bạn đang cung cấp. Bây giờ câu hỏi đặt ra: phải làm gì để giữ liên kết ID cũ?
Giả sử chúng ta muốn UPSERT với dữ liệu user_name = 'steven' và age = 32.
Nhìn vào mã này:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
Bí quyết là liên kết với nhau. Nó trả về id của người dùng 'steven' nếu có, và nếu không, nó trả về một id mới mới.
Tùy chọn 2: Bạn không thể xóa hàng
Sau khi xoay sở với giải pháp trước đó, tôi nhận ra rằng trong trường hợp của tôi, điều đó có thể kết thúc việc phá hủy dữ liệu, vì ID này hoạt động như một khóa ngoại cho bảng khác. Bên cạnh đó, tôi đã tạo bảng với mệnh đề ON DELETE CASCADE , có nghĩa là nó sẽ xóa dữ liệu một cách âm thầm. Nguy hiểm.
Vì vậy, đầu tiên tôi nghĩ đến một mệnh đề IF, nhưng SQLite chỉ có CASE . Và không thể sử dụng CASE này (hoặc ít nhất là tôi không quản lý nó) để thực hiện một truy vấn CẬP NHẬT nếu TỒN TẠI (chọn id từ những người chơi mà user_name = 'steven') và CHÈN nếu không. Không đi.
Và sau đó, cuối cùng tôi đã sử dụng tính vũ phu, thành công. Logic là, đối với mỗi UPSERT mà bạn muốn thực hiện, trước tiên hãy thực hiện CHÈN HOẶC BỎ QUA để đảm bảo rằng có một hàng với người dùng của chúng tôi, sau đó thực hiện truy vấn CẬP NHẬT với chính xác dữ liệu mà bạn đã cố gắng chèn.
Dữ liệu tương tự như trước: user_name = 'steven' và age = 32.
-- make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);
-- make sure it has the right data
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven';
Và đó là tất cả!
BIÊN TẬP
Như Andy đã nhận xét, cố gắng chèn trước và sau đó cập nhật có thể dẫn đến việc kích hoạt trình kích hoạt thường xuyên hơn dự kiến. Theo tôi, đây không phải là vấn đề an toàn dữ liệu, nhưng đúng là việc kích hoạt các sự kiện không cần thiết có rất ít ý nghĩa. Do đó, một giải pháp cải tiến sẽ là:
-- Try to update any existing row
UPDATE players SET age=32 WHERE user_name='steven';
-- Make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);