Mik*_*der 505 sql sqlite upsert
http://en.wikipedia.org/wiki/Upsert
在SQLite中有没有一些我没想到的聪明方法?
基本上,如果记录存在,我想更新四列中的三列,如果它不存在,我想用第四列的默认(NUL)值插入记录.
ID是主键,因此UPSERT只会有一条记录.
(我试图避免SELECT的开销,以确定我是否需要更新或插入显然)
建议?
我无法在SQLite网站上确认SQL CREATE的语法.我还没有构建一个演示来测试它,但它似乎不支持..
如果是,我有三列,所以它实际上看起来像:
CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    Blob1 BLOB ON CONFLICT REPLACE, 
    Blob2 BLOB ON CONFLICT REPLACE, 
    Blob3 BLOB 
);
但是前两个blob不会引起冲突,只有ID会因此我asusme Blob1和Blob2不会被替换(根据需要)
绑定数据时SQLite中的UPDATE是一个完整的事务,这意味着每个要更新的发送行都需要:Prepare/Bind/Step/Finalize语句,与允许使用重置函数的INSERT不同
语句对象的生命周期如下:
更新我猜测与INSERT相比速度慢,但它与使用主键的SELECT相比如何呢?
也许我应该使用select来读取第4列(Blob3),然后使用REPLACE编写一条新记录,将原始的第4列与前3列的新数据混合在一起?
小智 832
假设表中有3列.. ID,NAME,ROLE
BAD:这将使用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');
好的:这将更新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')
          );
gre*_*lom 127
INSERT或REPLACE 不等同于"UPSERT".
假设我有表Employee,其中包含字段id,name和role:
INSERT OR REPLACE INTO Employee ("id", "name", "role") VALUES (1, "John Foo", "CEO")
INSERT OR REPLACE INTO Employee ("id", "role") VALUES (1, "code monkey")
Boom,你已经丢失了员工编号1的名称.SQLite已将其替换为默认值.
UPSERT的预期输出是改变角色并保留名称.
Ari*_*zis 106
如果你想保留现有行中的一列或两列,Eric B的答案是可以的.如果你想保留很多列,它会变得太麻烦.
这是一种可以很好地扩展到任何一侧任意数量的列的方法.为了说明它,我将假设以下架构:
 CREATE TABLE page (
     id      INTEGER PRIMARY KEY,
     name    TEXT UNIQUE,
     title   TEXT,
     content TEXT,
     author  INTEGER NOT NULL REFERENCES user (id),
     ts      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
 );
特别注意,这name是行的自然键 - id仅用于外键,因此SQLite在插入新行时自行选择ID值.但是当基于它更新现有行时name,我希望它继续具有旧的ID值(显然!).
我UPSERT使用以下构造实现了一个真实的:
 WITH new (name, title, author) AS ( VALUES('about', 'About this site', 42) )
 INSERT OR REPLACE INTO page (id, name, title, content, author)
 SELECT old.id, new.name, new.title, old.content, new.author
 FROM new LEFT JOIN page AS old ON new.name = old.name;
此查询的确切形式可能有所不同.关键是使用INSERT SELECT左外连接,将现有行连接到新值.
在这里,如果行以前不存在,old.id会NULL再SQLite的会自动分配一个ID,但如果已经有了这样的行,old.id将有一个实际值,这将被重用.这正是我想要的.
实际上这非常灵活.请注意ts列的各个方面是如何完全丢失的 - 因为它有一个DEFAULT值,SQLite在任何情况下都会做正确的事情,所以我不必自己处理它.
您还可以同时在一列new和old面,然后用如COALESCE(new.content, old.content)在外SELECT说"插入新的内容,如果有任何,否则保留旧的内容" -例如,如果您使用的是固定的查询,并结合新的带占位符的值.
Sam*_*ron 81
如果你一般都在做更新,我会..
如果你通常做插入我会
这样你就可以避免选择,并且你在Sqlite上具有事务性.
Ant*_*ert 78
2018-05-18 STOP PRESS.
在SQLite中支持UPSERT!UPSERT语法已添加到SQLite版本3.24.0(待定)!
UPSERT是INSERT的一种特殊语法添加,如果INSERT违反唯一性约束,则会导致INSERT表现为UPDATE或no-op.UPSERT不是标准SQL.SQLite中的UPSERT遵循PostgreSQL建立的语法.
我知道我迟到了,但......
__PRE__
因此它会尝试更新,如果记录在那里则插入不会被动作.
或者:
另一种完全不同的方法是:在我的应用程序中,当我在内存中创建行时,我将内存rowID设置为long.MaxValue.(MaxValue将永远不会被用作ID,你将不会活得足够长....然后如果rowID不是那个值,那么它必须已经在数据库中,所以如果它是MaxValue需要更新,那么它需要插入.这仅在您可以跟踪应用中的rowID时有用.
Chr*_*los 59
我意识到这是一个旧线程,但我最近一直在sqlite3中工作,并提出了这个方法,它更适合我动态生成参数化查询的需求:
insert or ignore into <table>(<primaryKey>, <column1>, <column2>, ...) values(<primaryKeyValue>, <value1>, <value2>, ...); 
update <table> set <column1>=<value1>, <column2>=<value2>, ... where changes()=0 and <primaryKey>=<primaryKeyValue>; 
它仍然是2个查询,在更新中有一个where子句,但似乎可以解决问题.我也有这样的想法,如果对changes()的调用大于零,sqlite可以完全优化更新语句.它是否真的这样做是我所不知道的,但是一个人可以梦想不能吗?;)
对于奖励积分,您可以附加此行,该行返回行的ID,无论是新插入的行还是现有行.
select case changes() WHEN 0 THEN last_insert_rowid() else <primaryKeyValue> end;
Dav*_*err 13
这是一个真正的UPSERT(UPDATE或INSERT)而不是INSERT OR REPLACE(在许多情况下工作方式不同)的解决方案.
它的工作方式如下:
1.尝试更新具有相同Id的记录是否存在.
2.如果更新未更改任何行(NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0)),则插入记录.
因此,要么更新现有记录,要么执行插入.
重要的细节是使用changes()SQL函数来检查update语句是否命中了任何现有记录,如果没有命中任何记录,则只执行insert语句.
有一点需要注意的是,changes()函数不会返回由较低级别触发器执行的更改(请参阅http://sqlite.org/lang_corefunc.html#changes),因此请务必将其考虑在内.
这是SQL ...
测试更新:
--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
  [Id] INTEGER PRIMARY KEY, 
  [Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');
-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 2;
-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 2, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
--See the result
SELECT * FROM Contact;
测试插页:
--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
  [Id] INTEGER PRIMARY KEY, 
  [Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');
-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 3;
-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 3, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
--See the result
SELECT * FROM Contact;
从文档中:
UPSERT是INSERT的特殊语法补充,如果INSERT违反唯一性约束,则它会使INSERT表现为UPDATE或no-op。UPSERT不是标准的SQL。SQLite中的UPSERT遵循PostgreSQL建立的语法。UPSERT语法已添加到SQLite版本3.24.0(待定)中。
UPSERT是普通的INSERT语句,后跟特殊的ON CONFLICT子句
图片来源:https : //www.sqlite.org/images/syntax/upsert-clause.gif
您确实可以在SQLite中进行更新,它看起来与您习惯的有所不同。它看起来像:
INSERT INTO table name (column1, column2) 
VALUES ("value12", "value2") WHERE id = 123 
ON CONFLICT DO UPDATE 
SET column1 = "value1", column2 = "value2" WHERE id = 123
我所知道的最好的方法是进行更新,然后进行插入。“选择的开销”是必要的,但这并不是一个可怕的负担,因为您正在搜索主键,这很快。
您应该能够使用您的表和字段名称修改以下语句以执行您想要的操作。
--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
                );
小智 5
扩展亚里士多德的答案,你可以从一个虚拟的'singleton'表中选择(一个你自己创建的表,只有一行).这避免了一些重复.
我还将示例保持在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";
| 归档时间: | 
 | 
| 查看次数: | 277859 次 | 
| 最近记录: |