319

我已经为经典的“如何插入新记录或更新已存在的记录”找到了一些“将成为”解决方案,但我无法让它们中的任何一个在 SQLite 中工作。

我有一个定义如下的表:

CREATE TABLE Book 
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER

我想要做的是添加一个具有唯一名称的记录。如果名称已经存在,我想修改字段。

有人可以告诉我该怎么做吗?

4

9 回答 9

355

看看http://sqlite.org/lang_conflict.html

你想要这样的东西:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);

请注意,如果该行已存在于表中,则任何不在插入列表中的字段都将设置为 NULL。这就是该ID列有一个子选择的原因:在替换情况下,语句会将其设置为 NULL,然后分配一个新的 ID。

如果要在替换情况下的行但在插入情况下将该字段设置为 NULL,则也可以使用此方法。

例如,假设您想Seen独自离开:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
   (select ID from Book where Name = "SearchName"),
   "SearchName",
    5,
    6,
    (select Seen from Book where Name = "SearchName"));
于 2010-09-03T10:55:02.250 回答
109

您应该在INSERT OR IGNORE命令后使用UPDATE命令:在以下示例name中是主键:

INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'

第一个命令将插入记录。如果记录存在,它将忽略与现有主键冲突导致的错误。

第二个命令将更新记录(现在肯定存在)

于 2013-12-01T09:48:18.067 回答
73

您需要在表上设置约束以触发“冲突”,然后通过替换来解决:

CREATE TABLE data   (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);

然后你可以发出:

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);

“SELECT * FROM data”将为您提供:

2|2|2|3.0
3|1|2|5.0

请注意,data.id 是“3”而不是“1”,因为 REPLACE 执行的是 DELETE 和 INSERT,而不是 UPDATE。这也意味着您必须确保定义所有必要的列,否则您将获得意外的 NULL 值。

于 2011-01-27T10:54:28.540 回答
40

首先更新一下。如果受影响的行数= 0,则插入它。它是最简单且适用于所有RDBMS的。

于 2010-09-03T10:50:07.407 回答
37

INSERT OR REPLACE将其他字段替换为默认值。

sqlite> CREATE TABLE Book (
  ID     INTEGER PRIMARY KEY AUTOINCREMENT,
  Name   TEXT,
  TypeID INTEGER,
  Level  INTEGER,
  Seen   INTEGER
);

sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');

sqlite> SELECT * FROM Book;
1001|SQLite|||

如果要保留其他字段

  • 方法一
sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;

sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0
  • 方法二

使用UPSERT(语法已添加到 SQLite 版本 3.24.0 (2018-06-04))

INSERT INTO Book (ID, Name)
  VALUES (1001, 'SQLite')
  ON CONFLICT (ID) DO
  UPDATE SET Name=excluded.Name;

excluded.前缀等于VALUES( ) 中的值'SQLite'

于 2014-05-15T09:12:52.217 回答
28

Upsert是你想要的。UPSERT语法已添加到版本 3.24.0 (2018-06-04) 的 SQLite。

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

请注意,此时实际单词“UPSERT”不是 upsert 语法的一部分。

正确的语法是

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

如果你正在做INSERT INTO SELECT ...你的选择,至少需要用连接语法WHERE true解决解析器关于令牌的歧义。ON

请注意,INSERT OR REPLACE...如果必须替换,将在插入新记录之前删除记录,如果您有外键级联或其他删除触发器,这可能会很糟糕。

于 2018-10-31T16:42:29.457 回答
6

如果你没有主键,你可以插入,如果不存在,然后做一个更新。在使用此表之前,该表必须包含至少一个条目。

INSERT INTO Test 
   (id, name)
   SELECT 
      101 as id, 
      'Bob' as name
   FROM Test
       WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;

Update Test SET id='101' WHERE name='Bob';
于 2016-01-01T16:21:28.450 回答
4

我相信你想要UPSERT

在该答案中没有额外技巧的“插入或替换”会将您未指定的任何字段重置为 NULL 或其他默认值。(INSERT OR REPLACE 的这种行为与 UPDATE 不同;它与 INSERT 完全一样,因为它实际上是 INSERT;但是,如果您想要的是 UPDATE-if-exists,您可能需要 UPDATE 语义,并且会对实际结果感到不快。)

建议的 UPSERT 实现的技巧基本上是使用 INSERT OR REPLACE,但指定所有字段,使用嵌入式 SELECT 子句检索您不想更改的字段的当前值。

于 2012-08-02T23:15:36.367 回答
1

我认为值得指出的是,如果您不完全了解PRIMARY KEYUNIQUE如何交互,这里可能会出现一些意想不到的行为。

例如,如果您只想在当前未使用NAME字段的情况下插入一条记录,并且如果是,您希望触发一个约束异常来告诉您,那么INSERT OR REPLACE不会抛出异常,而是会通过替换冲突记录(具有相同NAME的现有记录)来解决UNIQUE约束本身。加斯帕德在上面的回答中很好地证明了这一点。

如果要触发约束异常,则必须使用INSERT语句,并在知道名称未被使用后依赖单独的UPDATE命令来更新记录。

于 2017-05-04T09:44:00.080 回答