假设我有一个这样创建的表:
CREATE TABLE actions (name text primary key, value text);
INSERT INTO actions (name) VALUES ('bla');
尝试插入新行时,有以下三种可能性:
我认为我应该能够使用这样的UPSERT语法:
INSERT INTO actions (name, value) values ('bla', 'val1')
ON CONFLICT(name) WHERE value IS NULL
DO UPDATE SET value = excluded.value;
INSERT INTO actions (name, value) values ('bla', 'val2')
ON CONFLICT(name) WHERE value IS NULL
DO UPDATE SET value = excluded.value;
第一条命令按预期更新现有行。但是第二个命令再次更新了该行,这没想到。
冲突目标中的where子句应该做什么?在我的测试中,它的值为true或false似乎无关紧要。
基于PostgreSQL Upsert with a WHERE clause,我尝试使用唯一的部分索引而不是主键,但是最后我得到了多个具有相同名称的行。
对我来说这似乎是个虫子。但是您可以轻松解决它:
INSERT INTO actions (name, value) values ('bla', 'val1')
ON CONFLICT(name)
DO UPDATE SET value = COALESCE(value, excluded.value);
Here是db <>小提琴。
ON CONFLICT
子句在检测到冲突后立即被调用,而与列的值无关。因此,您无法根据值决定以后是否进行更新或失败(即引发错误)。
一种解决方法是使用CASE
表达式仅分配先前是NULL
的新值:
INSERT INTO actions (name, value) values ('bla', 'val2')
ON CONFLICT(name)
DO UPDATE SET value = CASE WHEN value IS NULL THEN excluded.value ELSE value END;
如果初始值不为null,这不会失败,但这也不会正确分配新值。
我无法在语法图中实际上任何地方实际记录的冲突子句后立即找到WHERE
(但是,如果它的作用类似于Postgres,则表示不使用的部分索引;仍在研究之中) 。但是,使用second WHERE
将执行您想要的操作:
INSERT INTO actions(name) VALUES ('bla');
INSERT INTO actions(name, value) VALUES ('bla', 'val1')
ON CONFLICT(name) DO UPDATE SET value = excluded.value WHERE value IS NULL;
SELECT * FROM actions;
name value
---------- ----------
bla val1
INSERT INTO actions(name, value) VALUES ('bla', 'val2')
ON CONFLICT(name) DO UPDATE SET value = excluded.value WHERE value IS NULL;
SELECT * FROM actions;
name value
---------- ----------
bla val1
UPSERT
的文档没有阐明“冲突目标”]内的UPSERT
子句的使用,但是您可以通过删除该WHERE
子句并将其添加到以下位置来获得所需的功能。声明的结尾:
WHERE
请参见
INSERT INTO actions (name, value) values ('bla', 'val1') ON CONFLICT(name) DO UPDATE SET value = excluded.value WHERE value IS NULL; INSERT INTO actions (name, value) values ('bla', 'val2') ON CONFLICT(name) DO UPDATE SET value = excluded.value WHERE value IS NULL;
。