假设我有一个这样创建的表:
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,这不会失败,但这也不会正确分配新值。