http://en.wikipedia.org/wiki/Upsert
在SQL Server上插入更新存储过程
在SQLite中是否有一些我没有想到的聪明的方法来做到这一点?
基本上,如果记录存在,我想要更新四列中的三列,
如果它不存在,我想插入记录的默认值(NUL)为第四列。
ID是一个主键,所以UPSERT只会有一条记录。
(我试图避免选择的开销,以确定我是否需要更新或插入明显)
建议吗?
我无法确认在SQLite网站上创建表的语法。
我还没有构建一个演示来测试它,但它似乎不受支持。
如果是的话,我有三列,所以它实际上是这样的:
CREATE TABLE table1(
id INTEGER PRIMARY KEY ON CONFLICT REPLACE,
Blob1 BLOB ON CONFLICT REPLACE,
Blob2 BLOB ON CONFLICT REPLACE,
Blob3 BLOB
);
但前两个斑点不会引起冲突,只有ID会
所以我假设Blob1和Blob2不会被替换(如期望的那样)
SQLite中绑定数据时的更新是一个完整的事务,也就是说
每个要更新的发送行都需要:Prepare/Bind/Step/Finalize语句
与允许使用重置函数的INSERT不同
语句对象的生命周期是这样的:
使用sqlite3_prepare_v2()创建对象
使用sqlite3_bind_接口将值绑定到主机参数。
通过调用sqlite3_step()运行SQL
使用sqlite3_reset()重置语句,然后返回步骤2并重复。
使用sqlite3_finalize()销毁语句对象。
更新我猜是慢与插入相比,但它如何比较选择使用主键?
也许我应该使用选择读取第4列(Blob3),然后使用REPLACE写一个新记录混合原始的第4列与前3列的新数据?
假设表中有三列:ID、NAME、ROLE
坏:这将插入或替换所有列为ID=1的新值:
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES (1, 'John Foo', 'CEO');
坏:这将插入或替换2列…NAME列将被设置为NULL或默认值:
INSERT OR REPLACE INTO Employee (id, role)
VALUES (1, 'code monkey');
好:在冲突子句上使用SQLite
在SQLite中支持UPSERT !UPSERT语法在3.24.0版本添加到SQLite !
UPSERT是INSERT的一个特殊语法,如果INSERT违反唯一性约束,它会导致INSERT行为为UPDATE或no-op。UPSERT不是标准SQL。SQLite中的UPSERT遵循PostgreSQL建立的语法。
好但乏味:这将更新2列。
当ID=1存在时,NAME不受影响。
当ID=1不存在时,名称将为默认值(NULL)。
INSERT OR REPLACE INTO Employee (id, role, name)
VALUES ( 1,
'code monkey',
(SELECT name FROM Employee WHERE id = 1)
);
这将更新其中的2列。
当ID=1存在时,ROLE不受影响。
当ID=1不存在时,角色将被设置为'Benchwarmer',而不是默认值。
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES ( 1,
'Susan Bar',
COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
);
假设表中有三列:ID、NAME、ROLE
坏:这将插入或替换所有列为ID=1的新值:
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES (1, 'John Foo', 'CEO');
坏:这将插入或替换2列…NAME列将被设置为NULL或默认值:
INSERT OR REPLACE INTO Employee (id, role)
VALUES (1, 'code monkey');
好:在冲突子句上使用SQLite
在SQLite中支持UPSERT !UPSERT语法在3.24.0版本添加到SQLite !
UPSERT是INSERT的一个特殊语法,如果INSERT违反唯一性约束,它会导致INSERT行为为UPDATE或no-op。UPSERT不是标准SQL。SQLite中的UPSERT遵循PostgreSQL建立的语法。
好但乏味:这将更新2列。
当ID=1存在时,NAME不受影响。
当ID=1不存在时,名称将为默认值(NULL)。
INSERT OR REPLACE INTO Employee (id, role, name)
VALUES ( 1,
'code monkey',
(SELECT name FROM Employee WHERE id = 1)
);
这将更新其中的2列。
当ID=1存在时,ROLE不受影响。
当ID=1不存在时,角色将被设置为'Benchwarmer',而不是默认值。
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES ( 1,
'Susan Bar',
COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
);
扩展亚里士多德的答案,你可以从一个虚拟的“单行”表(你自己创建的单行表)中选择。这避免了一些重复。
我还保留了示例在MySQL和SQLite之间的可移植性,并使用“date_added”列作为如何仅第一次设置列的示例。
REPLACE INTO page (
id,
name,
title,
content,
author,
date_added)
SELECT
old.id,
"about",
"About this site",
old.content,
42,
IFNULL(old.date_added,"21/05/2013")
FROM singleton
LEFT JOIN page AS old ON old.name = "about";
我所知道的最佳方法是先进行更新,然后进行插入。
“选择的开销”是必要的,但这并不是一个可怕的负担,因为您是在快速的主键上搜索。
你应该能够用你的表和字段名修改下面的语句来做你想做的事情。
--first, update any matches
UPDATE DESTINATION_TABLE DT
SET
MY_FIELD1 = (
SELECT MY_FIELD1
FROM SOURCE_TABLE ST
WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
)
,MY_FIELD2 = (
SELECT MY_FIELD2
FROM SOURCE_TABLE ST
WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
)
WHERE EXISTS(
SELECT ST2.PRIMARY_KEY
FROM
SOURCE_TABLE ST2
,DESTINATION_TABLE DT2
WHERE ST2.PRIMARY_KEY = DT2.PRIMARY_KEY
);
--second, insert any non-matches
INSERT INTO DESTINATION_TABLE(
MY_FIELD1
,MY_FIELD2
)
SELECT
ST.MY_FIELD1
,NULL AS MY_FIELD2 --insert NULL into this field
FROM
SOURCE_TABLE ST
WHERE NOT EXISTS(
SELECT DT2.PRIMARY_KEY
FROM DESTINATION_TABLE DT2
WHERE DT2.PRIMARY_KEY = ST.PRIMARY_KEY
);