SQLite-UPSERT *不是* INSERT或REPLACE


535

http://en.wikipedia.org/wiki/Upsert

在SQL Server上插入更新存储的proc

在我没有想到的SQLite中,有什么聪明的方法可以做到这一点吗?

基本上,如果记录存在,我想更新四列中的三列;如果不存在,我想使用第四列的默认(NUL)值插入记录。

ID是一个主键,因此UPSERT只会有一个记录。

(我试图避免SELECT的开销,以便确定是否需要显式更新或插入)

有什么建议吗?


我无法在SQLite网站上确认TABLE 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会发生冲突,因此我不会替换Blob1和Blob2(根据需要)


当绑定数据是一个完整的事务时,SQLite中的UPDATE意味着要更新的每个发送行都需要:Prepare / Bind / Step / Finalize语句,与INSERT不同,后者允许使用reset函数

语句对象的寿命如下所示:

  1. 使用sqlite3_prepare_v2()创建对象
  2. 使用sqlite3_bind_接口将值绑定到主机参数。
  3. 通过调用sqlite3_step()运行SQL
  4. 使用sqlite3_reset()重置该语句,然后返回到步骤2并重复。
  5. 使用sqlite3_finalize()销毁语句对象。

我猜测UPDATE与INSERT相比比较慢,但是与使用主键的SELECT相比又如何呢?

也许我应该使用select读取第4列(Blob3),然后使用REPLACE编写一条将原始第4列与前3列的新数据混合在一起的新记录?



3
在SQLite的
3.24.0

Answers:


854

假设表中有三列:ID,NAME,ROLE


错误:这将用ID = 1的新值插入或替换所有列:

INSERT OR REPLACE INTO Employee (id, name, role) 
  VALUES (1, 'John Foo', 'CEO');

BAD:这将插入或替换2列... NAME列将设置为NULL或默认值:

INSERT OR REPLACE INTO Employee (id, role) 
  VALUES (1, 'code monkey');

好的在SQLite中使用SQLite On冲突子句 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时,角色将不受影响。当ID = 1不存在时,角色将设置为“ Benchwarmer”,而不是默认值。

INSERT OR REPLACE INTO Employee (id, name, role) 
  VALUES (  1, 
            'Susan Bar',
            COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
          );

33
+1辉煌!如果您需要组合/比较任何字段的旧值和新值,则内嵌的select子句可让您灵活地覆盖默认的ON CONFLICT REPLACE功能。
G__

24
如果“雇员”被级联删除的其他行引用,则其他行仍将通过替换删除。
唐·雷巴

246
这很伤人。SQlite需要UPSERT。
andig

21
你能解释一下为什么这将插入或与ID = 1的新值替换所有列:被认为是在你的第一个例子吗?您在此处显示的命令旨在创建一个ID为1的新记录,命名为John Foo并担任CEO角色,或者使用该数据覆盖ID为1的记录(如果该记录已经存在)(假设id是主键) 。那么,如果真的发生这种情况,那为什么不好呢?
OR Mapper 2014年

10
@Cornelius:很明显,但这不是第一个示例中发生的情况。第一个示例旨在强制设置所有列,无论插入记录还是替换记录,这都是事实。那么,为什么认为这不好呢?链接的答案还仅指出了为什么在指定列的子集时可能会发生不良情况,例如第二个示例;在为所有列指定值的同时,它似乎并未详细说明第一个示例中发生的任何不良影响。INSERT OR REPLACE
OR Mapper 2014年

134

INSERT或REPLACE 等同于“ UPSERT”。

假设我有表Employee,其字段ID,名称和角色为:

INSERT OR REPLACE INTO Employee ("id", "name", "role") VALUES (1, "John Foo", "CEO")
INSERT OR REPLACE INTO Employee ("id", "role") VALUES (1, "code monkey")

繁荣,您丢失了员工编号1的名称。SQLite已将其替换为默认值。

UPSERT的预期输出将是更改角色并保留名称。


21
我怕-1。是的,接受的答案是错误的,但是当您的答案指出问题时,它也不是答案。有关实际答案,请参见Eric B使用嵌入式coalesce((select ..), 'new value')子句的巧妙解决方案。我认为,埃里克(Eric)的答案需要更多票。

22
确实。埃里克(Eric)绝对是最好的答案,值得更多投票。话虽如此,我认为通过指出问题,我为找到正确的答案做出了一点贡献(Eric的答案随后出现,并基于我的答案中的示例sql表)。所以不确定我是否应得-1,但没关系:)
gregschlom 2011年

6
+1以抵消上面的-1。大声笑。不过,该线程上有趣的时间表。显然,您的回答比埃里克的回答快了一个星期,但是你们都在被问了将近两年之后回答了这个问题。埃里克也为此+1。
丰富


2
@QED否,因为delete + insert(这是一个替换)是2个dml语句,例如带有自己的触发器。它仅与1条更新语句不同。
塞巴斯

109

如果您只想保留现有行中的一列或两列,那么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.idNULL再SQLite的会自动分配一个ID,但如果已经有了这样的行,old.id将有一个实际值,这将被重用。这正是我想要的。

实际上,这是非常灵活的。请注意,该ts列在所有方面都是完全缺失的–因为它具有DEFAULT价值,所以SQLite在任何情况下都将做正确的事,因此我不必自己照顾它。

您还可以在newold两侧都包含一列,然后COALESCE(new.content, old.content)在外部使用例如SELECT“如果有则插入新内容,否则保留旧内容” –例如,如果您使用固定查询并绑定新内容占位符的值。


13
+1效果不错,但在上加了一个WHERE name = "about"约束SELECT ... AS old以加快处理速度。如果您有1m +行,这将非常慢。

好点,您的评论+1。不过,我将其排除在答案之外,因为添加这样的WHERE子句只需要我想出这种方法时首先要消除的那种查询冗余。与往常一样:在需要性能的情况下,可以对查询的结构进行非规范化。
亚里斯多德·帕加尔兹

4
如果需要,您可以简化亚里士多德的示例,例如: INSERT OR REPLACE INTO page (id, name, title, content, author) SELECT id, 'about', 'About this site', content, 42 FROM ( SELECT NULL ) LEFT JOIN ( SELECT * FROM page WHERE name = 'about' )
jcox

4
ON DELETE当执行替换(即更新)时,这是否会不必要地触发触发器?
Karakuri

3
它肯定会触发ON DELETE触发器。邓诺关于不必要的。对于大多数用户而言,这可能是不必要的,甚至是不必要的,但可能并非对所有用户而言。同样,由于它还会将具有外键的任何行级联删除到所讨论的行中,这可能是许多用户遇到的问题。不幸的是,SQLite离真正的UPSERT更近了。(INSTEAD OF UPDATE我猜是用触发器来伪造它的。)
亚里斯多德·帕加尔兹

86

如果您通常在进行更新,我会..

  1. 开始交易
  2. 做更新
  3. 检查行数
  4. 如果为0,则执行插入
  5. 承诺

如果您通常在做插入,我会

  1. 开始交易
  2. 尝试插入
  3. 检查主键违规错误
  4. 如果我们遇到错误,请执行更新
  5. 承诺

这样,您可以避免选择,并且在Sqlite上具有事务性。


谢谢,只问了这个@ stackoverflow.com / questions / 2717590 /…。向我+1!=)
Alix Axel

4
如果要在第三步使用sqlite3_changes()检查行数,请确保不要使用多个线程的数据库句柄进行修改。
Linulin

3
下列内容是否会带来一些麻烦:1)选择id ='x'的id表格2)如果(ResultSet.rows.length == 0)更新id ='x'的表格;
弗洛林2010年

20
我真的希望INSERT OR UPDATE是该语言的一部分
Florin 2010年

最终对我来说效果最好,尝试执行REPLACE INTO或始终执行替换操作,而当我主要执行更新而不是插入操作时,insert / update损害了我的性能。
PherricOxide

83

该答案已经更新,因此下面的评论不再适用。

2018-05-18停止新闻。

SQLite中的UPSERT支持!UPSERT语法已添加到SQLite版本3.24.0(待定)中!

UPSERT是INSERT的特殊语法补充,如果INSERT违反唯一性约束,则它会导致INSERT表现为UPDATE或no-op。UPSERT不是标准的SQL。SQLite中的UPSERT遵循PostgreSQL建立的语法。

在此处输入图片说明

或者:

另一个完全不同的方法是:在我的应用程序中,当我在内存中创建行时,将我在内存中的rowID设置为long.MaxValue。(MaxValue永远不会用作ID,您的寿命将不够长...。然后,如果rowID不是该值,则它必须已经存在于数据库中,因此如果它是MaxValue,则需要进行UPDATE,然后需要插入。仅当您可以跟踪应用程序中的rowID时,此功能才有用。


5
阿们 简单胜于复杂。这比接受的答案要简单一些。
kkurian

4
我以为您不能插入...在sqlite中的什么位置?这是我在sqlite3中遇到的语法错误
查理·马丁

5
@CharlieMartin是正确的。此语法对SQLite无效-这是OP要求的语法。一个WHERE子句不能被附加到一个INSERT说法: sqlite的嵌件 ...
colm.anseo

4
这个答案已经让我失去了很多时间,问题是关于SQLite和我不知道地方插入未在sqite支持,请添加此请注意,它是在你的答案源码无效
米克尔

3
@colminator和其他人:我根据您的建议修复了他的SQL语句。
F Lekschas

61

我意识到这是一个旧线程,但是最近我一直在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;

+ 1.正是我要替换的一些SQL Server TSQL代码进行的转换。谢谢。我试图替换的SQL就像Update <statement> If @@ROWCOUNT=0 INSERT INTO <statement>
DarrenMB 2014年

14

这是一个实际上是UPSERT(更新或INSERT)而不是INSERT或REPLACE(在许多情况下工作原理不同)的解决方案。

它的工作原理如下:
1.尝试更新是否存在具有相同ID的记录。
2.如果更新未更改任何行(NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0)),则插入记录。

因此,要么更新现有记录,要么执行插入操作。

重要的细节是使用change()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;

2
对于我来说,这似乎是比Eric更好的解决方案。但是INSERT INTO Contact (Id, Name) SELECT 3, 'Bob' WHERE changes() = 0;应该也可以。
bkausbk

谢谢您,它也可以在WebSQL中使用(使用Cordova和SQLite插件)
Zappescu

11

从3.24.0版本开始,SQLite支持UPSERT。

文档中

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


8

您确实可以在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

5

扩展亚里士多德的答案,您可以从虚拟“单人”表(您自己创建的具有一行的表)中进行选择。这样可以避免重复。

我还保留了该示例在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";

4

我知道最好的方法是先进行更新,然后进行插入。“选择的开销”是必要的,但这并不是一个沉重的负担,因为您正在快速地搜索主键。

您应该能够使用表和字段名称修改以下语句以执行所需的操作。

--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
                );

7
方式复杂。
10年

我认为这不是一个好主意,因为您需要对数据库引擎执行两次请求。
Ricardo da Rocha Vitor

3

如果有人想在Cordova中阅读我的SQLite解决方案,则感谢上面的@david回答,我得到了这种通用的js方法。

function    addOrUpdateRecords(tableName, values, callback) {
get_columnNames(tableName, function (data) {
    var columnNames = data;
    myDb.transaction(function (transaction) {
        var query_update = "";
        var query_insert = "";
        var update_string = "UPDATE " + tableName + " SET ";
        var insert_string = "INSERT INTO " + tableName + " SELECT ";
        myDb.transaction(function (transaction) {
            // Data from the array [[data1, ... datan],[()],[()]...]:
            $.each(values, function (index1, value1) {
                var sel_str = "";
                var upd_str = "";
                var remoteid = "";
                $.each(value1, function (index2, value2) {
                    if (index2 == 0) remoteid = value2;
                    upd_str = upd_str + columnNames[index2] + "='" + value2 + "', ";
                    sel_str = sel_str + "'" + value2 + "', ";
                });
                sel_str = sel_str.substr(0, sel_str.length - 2);
                sel_str = sel_str + " WHERE NOT EXISTS(SELECT changes() AS change FROM "+tableName+" WHERE change <> 0);";
                upd_str = upd_str.substr(0, upd_str.length - 2);
                upd_str = upd_str + " WHERE remoteid = '" + remoteid + "';";                    
                query_update = update_string + upd_str;
                query_insert = insert_string + sel_str;  
                // Start transaction:
                transaction.executeSql(query_update);
                transaction.executeSql(query_insert);                    
            });
        }, function (error) {
            callback("Error: " + error);
        }, function () {
            callback("Success");
        });
    });
});
}

因此,首先使用此函数选择列名称:

function get_columnNames(tableName, callback) {
myDb.transaction(function (transaction) {
    var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
    transaction.executeSql(query_exec, [], function (tx, results) {
        var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
        var columnNames = [];
        for (i in columnParts) {
            if (typeof columnParts[i] === 'string')
                columnNames.push(columnParts[i].split(" ")[0]);
        };
        callback(columnNames);
    });
});
}

然后以编程方式构建事务。

“值”是一个应该在其之前构建的数组,它表示要在表中插入或更新的行。

因为我要与远程服务器同步,所以“ remoteid”是我用作参考的ID。

有关使用SQLite Cordova插件的信息,请参阅官方链接


2

此方法重新混合了该问题答案中的其他几种方法,并结合使用了CTE(公用表表达式)。我将介绍查询,然后解释为什么我做了我所做的事情。

如果有300名员工,我想将300名员工的姓氏更改为DAVIS。否则,我将添加一个新员工。

表名称:employees列:id,first_name,last_name

查询是:

INSERT OR REPLACE INTO employees (employee_id, first_name, last_name)
WITH registered_employees AS ( --CTE for checking if the row exists or not
    SELECT --this is needed to ensure that the null row comes second
        *
    FROM (
        SELECT --an existing row
            *
        FROM
            employees
        WHERE
            employee_id = '300'

        UNION

        SELECT --a dummy row if the original cannot be found
            NULL AS employee_id,
            NULL AS first_name,
            NULL AS last_name
    )
    ORDER BY
        employee_id IS NULL --we want nulls to be last
    LIMIT 1 --we only want one row from this statement
)
SELECT --this is where you provide defaults for what you would like to insert
    registered_employees.employee_id, --if this is null the SQLite default will be used
    COALESCE(registered_employees.first_name, 'SALLY'),
    'DAVIS'
FROM
    registered_employees
;

基本上,我使用CTE来减少必须使用select语句确定默认值的次数。由于这是CTE,因此我们只需从表中选择所需的列,而INSERT语句将使用此列。

现在,您可以通过将COALESCE函数中的空值替换为应设置的值来决定要使用的默认值。


2

亚里士多德Pagaltzis和的想法COALESCE来自埃里克B的答案,这里是一个UPSERT选项更新仅几列或插入整行,如果它不存在。

在这种情况下,假设标题和内容应该更新,保留现有的其他旧值,而在找不到名称时插入提供的旧值:

当NOTE应该 id被强制为NULL时INSERT,它被强制为NULL 。如果只是生成的主键,那么COALESCE也可以使用它(请参阅Aristotle Pagaltzis评论)。

WITH new (id, name, title, content, author)
     AS ( VALUES(100, 'about', 'About this site', 'Whatever new content here', 42) )
INSERT OR REPLACE INTO page (id, name, title, content, author)
SELECT
     old.id, COALESCE(old.name, new.name),
     new.title, new.content,
     COALESCE(old.author, new.author)
FROM new LEFT JOIN page AS old ON new.name = old.name;

因此,一般规则是,如果要保留旧值,请使用COALESCE,而要更新值时请使用new.fieldname


COALESCE(old.id, new.id)自动递增密钥肯定是错误的。虽然“保持大多数行不变,除了缺少值的地方”听起来确实有人用例,但我认为这并不是人们在寻找如何进行UPSERT时所要寻找的。
亚里斯多德·帕加尔兹

@AristotlePagaltzis道歉,如果我错了,我没有使用自动增量。我在此查询中寻找的是仅更新少量列或在不存在的情况下使用提供的值插入整行。我一直在处理您的查询,但在插入时无法实现:从old表中选择的列分配给NULL,而不是所提供的值new。这就是使用的原因COALESCE。我不是sqlite的专家,我一直在测试此查询,并且似乎可以解决问题,如果您能以自动递增的方式指向我,我将非常感谢您
Miquel

2
在自动增量键的情况下,你插入NULL的关键,因为这告诉SQLite的,而不是插入一个可用值。
亚里斯多德·帕加尔兹

我并不是说您不应该做自己正在做的事情(如果您需要它,那么就需要它),只是这并不是对这个问题的真正答案。UPSERT通常意味着您要在表中存储一行,而只是不知道是否已有匹配的行可将值放入其中或需要将其作为新行插入。您的用例是,如果您已经有一个匹配的行,则想忽略新行中的大多数值。很好,这不是问的问题。
亚里斯多德·帕加尔兹

1

我认为这可能是您要寻找的:ON CONFLICT子句

如果您这样定义表格:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    field1 TEXT 
); 

现在,如果您使用已经存在的ID进行INSERT,SQLite会自动进行UPDATE而不是INSERT。

嗯...


6
我认为这行不通,它将清除插入语句中丢失的列
Sam Saffron

3
@Mosor:对不起我,-1。这与发布REPLACE声明相同。
Alix Axel

7
-1,因为它执行删除操作,如果主键已经存在,则执行插入操作。
10年

0

如果您不介意在两个操作中执行此操作。

脚步:

1)使用“ INSERT OR IGNORE”添加新项目

2)使用“更新”更新现有项目

这两个步骤的输入是相同的新项目或可更新项目的集合。适用于不需要更改的现有项目。它们将被更新,但是具有相同的数据,因此最终结果没有变化。

当然,速度较慢等。效率低下。是的

易于编写sql并维护和理解它吗?绝对是

需要权衡考虑。非常适合小型ups。对于那些不介意牺牲代码可维护性效率的用户而言,它非常有用。


所有人注意:这篇文章不是关于INSERT或REPLACE的内容。INSERT OR REPLACE将在表中创建一个具有新ID的NEW行。那不是更新。
sapbucket

-2

刚读完此线程后,对于仅进行此“ UPSERT”操作并不容易感到失望,我进一步进行了调查...

实际上,您可以在SQLITE中直接轻松地做到这一点。

而不是使用: INSERT INTO

采用: INSERT OR REPLACE INTO

这正是您想要的!


21
-1 INSERT OR REPLACE不是UPSERT。有关原因,请参阅gregschlom的“答案”埃里克B(Eric B)的解决方案实际上是有效的,并且需要一些投票。

-4
SELECT COUNT(*) FROM table1 WHERE id = 1;

如果 COUNT(*) = 0

INSERT INTO table1(col1, col2, cole) VALUES(var1,var2,var3);

否则,如果 COUNT(*) > 0

UPDATE table1 SET col1 = var4, col2 = var5, col3 = var6 WHERE id = 1;

这太复杂了,SQL可以在一个查询中很好地解决这一问题
Robin Kanters
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.