对于经典的“如何插入新记录或更新已存在的记录”,我已经找到了一些“可能”的解决方案,但我无法让它们在SQLite中工作。
我有一个定义如下的表:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
字符串
我想做的是添加一个具有唯一名称的记录。如果“名称”已存在,我想修改字段。
有人能告诉我怎么做吗?
9条答案
按热度按时间fzwojiic1#
看看http://sqlite.org/lang_conflict.html。
你想要的是:
字符串
请注意,如果表中已存在行,则插入列表中未包含的任何字段都将设置为NULL。这就是为什么
ID
列有一个子选择:在替换的情况下,语句会将其设置为NULL,然后分配一个新的ID。如果您希望在替换情况下保留特定字段值,但在插入情况下将字段设置为NULL,也可以使用这种方法。
例如,假设你不想处理
Seen
:型
9gm1akwq2#
您应该使用
INSERT OR IGNORE
命令,然后使用UPDATE
命令:在下面的例子中,name
是一个主键:字符串
第一个命令将插入记录。如果该记录存在,它将忽略与现有主键冲突所导致的错误。
第二个命令将更新记录(现在肯定存在)
slwdgvem3#
你需要在表上设置一个约束来触发一个“conflict“,然后通过执行一个replace来解决这个问题:
字符串
然后,您可以发出:
型
“SELECT * FROM data”将为您提供:
型
请注意,data.id是“3”而不是“1”,因为REPLACE执行DELETE和INSERT,而不是UPDATE。这也意味着您必须确保定义了所有必要的列,否则将获得意外的NULL值。
polkgigr4#
INSERT OR REPLACE
会将其他字段替换为默认值。个字符
如果要保留其他字段
型
使用UPSERT(
UPSERT
已添加到SQLite版本3.24.0(2018-06-04))型
excluded.
前缀等于VALUES
('SQLite'
)中的值。ulmd4ohb5#
首先更新一下。如果affected row count= 0,则插入它。它是最简单的,适用于所有RDBMS。
dgjrabp26#
Upsert是你想要的。
UPSERT
语法被添加到SQLite 3.24.0版本(2018-06-04)。字符串
请注意,此时实际的单词“UPSERT”不是upsert语法的一部分。
正确的语法是
INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...
个如果你正在执行
INSERT INTO SELECT ...
,那么你的select至少需要WHERE true
来解决解析器关于join语法的标记ON
的二义性。请注意,如果必须替换,
INSERT OR REPLACE...
将在插入新记录之前删除该记录,如果您有外键级联或其他删除触发器,这可能是不好的。mfuanj7w7#
如果你没有主键,你可以插入如果不存在,然后做一个更新。在使用此之前,表必须至少包含一个条目。
字符串
mftmpeh88#
我相信你想要UPSERT。
“INSERT OR REPLACE”(没有附加技巧)会将任何未指定的字段重置为NULL或其他默认值。(INSERT OR REPLACE的这种行为与UPDATE不同;它和INSERT完全一样,因为它实际上就是INSERT;但是,如果您想要的是UPDATE-if-exists,那么您可能想要UPDATE语义,并且会对实际结果感到意外。)
建议的UPSERT实现的技巧基本上是使用INSERT OR REPLACE,但指定所有字段,使用嵌入的SELECT子句检索不想更改的字段的当前值。
vbkedwbf9#
我认为值得指出的是,如果您没有彻底了解PRIMARY KEY和UNIQUE如何交互,这里可能会出现一些意外行为。
例如,如果您只想在 NAME 字段当前未被占用时插入一条记录,并且如果是,您希望触发一个约束异常来告诉您,那么INSERT OR REPLACE将不会抛出异常,而是通过替换冲突记录(具有相同 NAME 的现有记录)来解决UNIQUE约束本身。Gaspard's在上面的his answer中很好地演示了这一点。
如果你想触发一个约束异常,你必须使用一个INSERT语句,并依靠一个单独的UPDATE命令来更新记录,一旦你知道这个名字没有被使用。