INSERT如果不是EXISTS ELSE UPDATE?

问题描述 投票:249回答:9

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

我有一个表定义如下:

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

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

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

sqlite insert exists
9个回答
306
投票

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

77
投票

您应该使用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'

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

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


61
投票

您需要在表上设置约束以触发“conflict”,然后通过执行替换来解析:

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值。


32
投票

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


16
投票

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

INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming')

我正在使用这个

INSERT OR IGNORE INTO book(id) VALUES(1001);
UPDATE book SET name = 'Programming' WHERE id = 1001;

你也可以使用

INSERT OR REPLACE INTO book (id, name) 
VALUES (1001, 'Programming',
  (SELECT typeid FROM book WHERE id = 1001),
  (SELECT level FROM book WHERE id = 1001),
)

但我认为第一种方法更容易阅读


7
投票

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

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...将在插入新记录之前删除记录,如果您有外键级联或其他删除触发器,这可能会很糟糕。


5
投票

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

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

3
投票

我相信你想要UPSERT

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

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


1
投票

我认为值得指出的是,如果你不完全理解PRIMARY KEY和UNIQUE如何相互作用,那么可能会有一些意想不到的行为。

例如,如果您只想在当前未采用NAME字段时插入记录,并且如果它是,您希望触发约束异常来告诉您,那么INSERT OR REPLACE将不会抛出异常而是通过替换冲突记录(具有相同NAME的现有记录)来解决UNIQUE约束本身。 Gaspard's在上面的his answer中表现得非常好。

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

© www.soinside.com 2019 - 2024. All rights reserved.