INSERT如果不是EXISTS ELSE UPDATE?

Spa*_*yNZ 256 sqlite insert exists

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

我有一个表定义如下:

CREATE TABLE Book 
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER
Run Code Online (Sandbox Code Playgroud)

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

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

jan*_*anm 309

看看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", ...);
Run Code Online (Sandbox Code Playgroud)

请注意,如果表中已存在该行,则不在插入列表中的任何字段将设置为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"));
Run Code Online (Sandbox Code Playgroud)

  • 错误的"插入或替换"与"插入或更新"不同.有关有效答案,请参阅http://stackoverflow.com/questions/418898/sqlite-upsert-not-insert-or-replace (104认同)
  • @rds不,这没错,因为这个问题说"修改字段"而主键不是列列表的一部分,但所有其他字段都是.如果你打算在没有替换所有字段值的情况下,或者如果你正在搞乱主键,你应该做一些不同的事情.如果您有一套完整的新字段,这种方法就可以了.你有一个我看不到的具体问题吗? (12认同)
  • 如果您知道所有字段的所有新值,则它是有效的.如果用户仅更新,例如,"Level",则不能遵循此方法. (8认同)
  • **正确**.另一个答案是相关的,但这种方法适用于更新所有字段(不包括密钥). (4认同)
  • 您可能希望添加一个陷阱,即 INSERT OR REPLACE 将使用被替换的记录作为外键来触发其他表上的 ON DELETE CASCADE 子句。结果有点令人困惑,直到您意识到“INSERT OR REPLACE”的行为(有意义)为“DELETE IF EXISTS, THEN INSERT”,它会触发对记录删除起作用的操作。 (3认同)
  • 是的这是完全错误的。如果只是我想从新的冲突更新单列值,将会发生什么。在上述情况下,所有其他数据将被不正确的新数据替换。 (2认同)

小智 80

您应该使用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'
Run Code Online (Sandbox Code Playgroud)

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

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

  • 它会忽略哪一刻?当名字和年龄都一样的时候? (6认同)
  • 当记录是新的时,那么更新不是必需的,但无论如何都会执行,导致性能不佳? (3认同)

gas*_*ard 62

您需要在表上设置约束以触发" 冲突 ",然后通过执行替换来解决此问题:

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);
Run Code Online (Sandbox Code Playgroud)

然后你可以发出:

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);
Run Code Online (Sandbox Code Playgroud)

"SELECT*FROM data"将为您提供:

2|2|2|3.0
3|1|2|5.0
Run Code Online (Sandbox Code Playgroud)

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


Bur*_*cin 32

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

  • 无论数据库如何,两个操作在正确的隔离级别上都应该没有问题. (11认同)
  • 另外,如果行值完全相同,那么受影响的行数将为零,并且将创建新的重复行. (6认同)
  • `插入或替换'确实更可取. (5认同)
  • 我真的希望 IT 文档包含更多示例。我在下面尝试过这个,但它不起作用(我的语法显然是错误的)。关于它应该是什么有什么想法吗?INSERT INTO Book (Name,TypeID,Level,Seen) VALUES( 'Superman', '2', '14', '0' ) ON CONFLICT REPLACE Book (Name,TypeID,Level,Seen) VALUES( 'Superman', ' 2'、'14'、'0') (2认同)
  • +1,因为**INSERT OR REPLACE**会删除冲突时的原始行,如果你没有设置所有列,你将失去原始值 (2认同)

Ste*_*ing 17

INSERT OR REPLACE将其他字段(TypeID,Level)替换为默认值.

INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming')
Run Code Online (Sandbox Code Playgroud)

我正在使用这个

INSERT OR IGNORE INTO book(id) VALUES(1001);
UPDATE book SET name = 'Programming' WHERE id = 1001;
Run Code Online (Sandbox Code Playgroud)

你也可以使用

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

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


Com*_*ool 10

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;
Run Code Online (Sandbox Code Playgroud)

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

正确的语法是

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

如果您正在执行INSERT INTO SELECT ...选择,则至少需要使用连接语法WHERE true解决有关令牌的解析器歧义ON

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


met*_*att 5

我相信你想要UPSERT

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

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


mat*_*att 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';
Run Code Online (Sandbox Code Playgroud)