我找到了一些经典的“将来”的解决方案,如果它已经存在,我将如何插入新记录或更新一个记录,但我无法让它们在SQLite中工作。
我有一个表定义如下:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
我想要做的是添加一个具有唯一名称的记录。如果名称已存在,我想修改字段。
有人可以告诉我该怎么做?
看看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"));
您应该使用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'
第一个命令将插入记录。如果记录存在,它将忽略与现有主键冲突导致的错误。
第二个命令将更新记录(现在肯定存在)
您需要在表上设置约束以触发“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值。
首先更新它。如果受影响的行数= 0,则插入它。它最简单,适用于所有RDBMS。
INSERT OR REPLACE
将其他字段(TypeID
,Level
)替换为默认值。
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),
)
但我认为第一种方法更容易阅读
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...
将在插入新记录之前删除记录,如果您有外键级联或其他删除触发器,这可能会很糟糕。
如果您没有主键,则可以插入(如果不存在),然后执行更新。在使用此表之前,该表必须至少包含一个条目。
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';
我相信你想要UPSERT。
“INSERT OR REPLACE”在该答案中没有额外的诡计会将您未指定的任何字段重置为NULL或其他默认值。 (INSERT OR REPLACE的这种行为与UPDATE不同;它与INSERT完全相同,因为它实际上是INSERT;但是如果您想要的是UPDATE-if-exists,您可能需要UPDATE语义并且会对实际结果感到不愉快。)
建议的UPSERT实现的技巧基本上是使用INSERT OR REPLACE,但指定所有字段,使用嵌入的SELECT子句来检索您不想更改的字段的当前值。
我认为值得指出的是,如果你不完全理解PRIMARY KEY和UNIQUE如何相互作用,那么可能会有一些意想不到的行为。
例如,如果您只想在当前未采用NAME字段时插入记录,并且如果它是,您希望触发约束异常来告诉您,那么INSERT OR REPLACE将不会抛出异常而是通过替换冲突记录(具有相同NAME的现有记录)来解决UNIQUE约束本身。 Gaspard's在上面的his answer中表现得非常好。
如果要触发约束异常,则必须使用INSERT语句,并且一旦知道未采用该名称,就依赖单独的UPDATE命令来更新记录。