问答风格
好了,在研究并解决了数小时的问题之后,我发现有两种方法可以完成此任务,具体取决于表的结构以及是否激活了外键限制以保持完整性。我想以干净的格式分享此信息,以节省一些可能遇到我的人的时间。
选项1:您可以删除该行
换句话说,您没有外键,或者如果您有外键,则对SQLite引擎进行配置,以使不存在完整性异常。要走的路是INSERT或REPLACE。如果您尝试插入/更新其ID已经存在的播放器,则SQLite引擎将删除该行并插入您提供的数据。现在的问题来了:如何使旧ID保持关联?
假设我们要使用数据user_name ='steven'和age = 32 进行UPSERT。
看下面的代码:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
诀窍正在融合。它返回用户'steven'的ID(如果有),否则返回新的新鲜ID。
选项2:您无法删除该行
在研究了先前的解决方案之后,我意识到在我的情况下最终可能会破坏数据,因为此ID充当其他表的外键。此外,我使用子句ON DELETE CASCADE创建了表,这意味着它将默默地删除数据。危险的。
因此,我首先想到了IF子句,但SQLite仅具有CASE。而且,如果有EXISTS(请从user_name ='steven'的玩家中选择ID )执行此更新,则不能使用此CASE(或至少我没有对其进行管理),否则请执行INSERT。不行
然后,最后我成功地使用了蛮力。逻辑是,对于您要执行的每个UPSERT,首先执行INSERT或IGNORE以确保与我们的用户一行,然后使用与您尝试插入的数据完全相同的数据执行UPDATE查询。
与以前相同的数据:user_name ='steven'和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';
就这样!
编辑
正如Andy所说,尝试先插入然后更新可能会导致触发触发器的次数比预期的多。我认为这不是数据安全问题,但触发不必要的事件确实没有任何意义。因此,一种改进的解决方案将是:
-- 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);