Q&A Стиль
Ну, вивчивши проблему та боровшись із проблемою протягом декількох годин, я з’ясував, що це можна досягти двома способами, залежно від структури вашої таблиці та якщо у вас активовані обмеження на зовнішні ключі для підтримки цілісності. Я хотів би поділитися цим в чистому форматі, щоб заощадити час людям, які можуть бути в моїй ситуації.
Варіант 1: Ви можете дозволити видалення рядка
Іншими словами, у вас немає зовнішнього ключа, або якщо у вас є, ваш двигун SQLite налаштований так, щоб не було винятків з цілісності. Шлях пройти - ВСТАВИТИ АБО ЗАМОВИТИ . Якщо ви намагаєтеся вставити / оновити програвач, ідентифікатор якого вже існує, движок SQLite видалить цей рядок і вставить дані, які ви надаєте. Тепер виникає питання: що робити, щоб зберегти старий ідентифікатор?
Скажімо, ми хочемо UPSERT з даними user_name = 'steven' та віком = 32.
Подивіться на цей код:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
Хитрість полягає в поєднанні. Він повертає ідентифікатор користувача "steven", якщо такий є, і в іншому випадку він повертає новий свіжий ідентифікатор.
Варіант 2: Ви не можете дозволити видалення рядка
Після роздумування з попереднім рішенням я зрозумів, що в моєму випадку це може призвести до знищення даних, оскільки цей ідентифікатор працює як зовнішній ключ для іншої таблиці. Крім того, я створив таблицю з пунктом ON DELETE CASCADE , що означатиме, що вона видаляє дані безшумно. Небезпечний.
Отже, я спершу придумав пункт IF, але у SQLite є лише CASE . І цей CASE не може бути використаний (або, принаймні, я не керував ним) для виконання одного UPDATE- запиту, якщо існує (виберіть ідентифікатор від гравців, де user_name = 'steven'), і INSERT, якщо він не був. Не йдіть.
І тоді, нарешті, я застосував грубу силу, з успіхом. Логіка полягає в тому, що для кожного UPSERT, який ви хочете виконати, спочатку виконайте INSERT OR IGNORE, щоб переконатися, що є ряд з нашим користувачем, а потім виконати запит UPDATE з точно тими ж даними, які ви намагалися вставити.
Ті ж дані, що і раніше: user_name = 'steven' та вік = 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';
І це все!
EDIT
Як прокоментував Енді, спроба вставити спочатку, а потім оновити, може призвести до запуску тригерів частіше, ніж очікувалося. На мою думку, це питання безпеки даних не є, але це правда, що звільнення непотрібних подій мало сенсу. Тому вдосконаленим рішенням було б:
-- 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);