bgu*_*ach 92 database sqlite upsert
我需要对SQLite数据库执行UPSERT/INSERT或UPDATE.
在许多情况下,命令INSERT OR REPLACE可能很有用.但是如果你想因为外键而使你的id保持自动增量,那么它不起作用,因为它删除了行,创建了一个新行,因此这个新行有一个新的ID.
这将是表格:
播放器 - (id上的主键,user_name唯一)
| id | user_name | age |
------------------------------
| 1982 | johnny | 23 |
| 1983 | steven | 29 |
| 1984 | pepee | 40 |
Run Code Online (Sandbox Code Playgroud)
bgu*_*ach 104
那么,研究和与时间问题战斗后,我发现有两种方法可以做到这一点,取决于你的表的结构,如果你有激活保持正直外键约束.我想以干净的格式分享这个,以节省一些可能在我的情况下的人.
换句话说,您没有外键,或者如果您有外键,则配置SQLite引擎,以便不存在完整性异常.要走的路是INSERT OR 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)
Run Code Online (Sandbox Code Playgroud)
诀窍是合并.它返回用户'steven'的id(如果有的话),否则返回一个新的id.
在使用之前的解决方案进行调整之后,我意识到在我的情况下最终可能会破坏数据,因为此ID可用作其他表的外键.此外,我使用ON DELETE CASCADE子句创建了表,这意味着它将以静默方式删除数据.危险的.
所以,我首先想到了一个IF子句,但SQLite只有CASE.如果EXISTS(从user_name ='steven'的玩家中选择id ),则不能使用此CASE(或者至少我没有管理它)执行一个UPDATE查询,如果没有则INSERT.不行.
然后,最后我使用了蛮力,并取得了成功.逻辑是,对于您要执行的每个UPSERT,首先执行INSERT或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';
Run Code Online (Sandbox Code Playgroud)
就这样!
正如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);
Run Code Online (Sandbox Code Playgroud)
Mar*_*eIV 67
这是一种不需要暴力"忽略"的方法,只有在存在密钥违规的情况下才会有效.这种方式基于您在更新中指定的任何条件.
试试这个...
-- Try to update any existing row
UPDATE players
SET age=32
WHERE user_name='steven';
-- If no update happened (i.e. the row didn't exist) then insert one
INSERT INTO players (user_name, age)
SELECT 'steven', 32
WHERE (Select Changes() = 0);
Run Code Online (Sandbox Code Playgroud)
这里的"魔术"是你使用该Where (Select Changes() = 0)子句来确定插入是否有任何行,并且由于这是基于你自己的Where子句,它可以是你定义的任何东西,而不仅仅是密钥违规.
在上面的示例中,如果更新没有更改(即记录不存在),则Changes()= 0,因此Where语句中的子句Insert返回true,并插入带有指定数据的新行.
如果Update 确实更新了现有行,那么Changes()= 1,因此现在的'Where'子句Insert将为false,因此不会发生插入.
不需要蛮力.
pra*_*pin 42
这是一个迟到的答案.从2018年6月4日发布的SQLIte 3.24.0开始,最终在PostgreSQL语法之后支持UPSERT子句.
INSERT INTO players (user_name, age)
VALUES('steven', 32)
ON CONFLICT(user_name)
DO UPDATE SET age=excluded.age;
Run Code Online (Sandbox Code Playgroud)
And*_*ndy 25
所有提出的答案的问题完全没有考虑到触发器(可能还有其他副作用).解决方案如
INSERT OR IGNORE ...
UPDATE ...
Run Code Online (Sandbox Code Playgroud)
当行不存在时,导致执行两个触发器(用于插入,然后用于更新).
适当的解决方案是
UPDATE OR IGNORE ...
INSERT OR IGNORE ...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,只执行一个语句(当行存在或不存在时).
拥有一个不带唯一键和其他键的无孔纯正UPSERT(对于程序员):
UPDATE players SET user_name="gil", age=32 WHERE user_name='george';
SELECT changes();
Run Code Online (Sandbox Code Playgroud)
SELECT changes()将返回上次查询中完成的更新次数。然后检查changes()的返回值是否为0,如果是,则执行:
INSERT INTO players (user_name, age) VALUES ('gil', 32);
Run Code Online (Sandbox Code Playgroud)
如果您想避免两者changes()=0,INSERT OR IGNORE即使您无法删除该行 - 您可以使用此逻辑;
首先,插入(如果不存在),然后通过使用唯一键进行过滤来更新。
-- Table structure
CREATE TABLE players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_name VARCHAR (255) NOT NULL
UNIQUE,
age INTEGER NOT NULL
);
-- Insert if NOT exists
INSERT INTO players (user_name, age)
SELECT 'johnny', 20
WHERE NOT EXISTS (SELECT 1 FROM players WHERE user_name='johnny' AND age=20);
-- Update (will affect row, only if found)
-- no point to update user_name to 'johnny' since it's unique, and we filter by it as well
UPDATE players
SET age=20
WHERE user_name='johnny';
Run Code Online (Sandbox Code Playgroud)
注意:我还没有测试它来查看正在调用哪些触发器,但我假设如下:
这样你就可以有一个单一的 SQL 命令
-- Table structure
CREATE TABLE players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_name VARCHAR (255) NOT NULL
UNIQUE,
age INTEGER NOT NULL
);
-- Single command to insert or update
INSERT OR REPLACE INTO players
(id, user_name, age)
VALUES ((SELECT id from players WHERE user_name='johnny' AND age=20),
'johnny',
20);
Run Code Online (Sandbox Code Playgroud)
编辑:添加选项 2。
| 归档时间: |
|
| 查看次数: |
90812 次 |
| 最近记录: |