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