use*_*146 1 sql postgresql null unique upsert
我有一个 Postgres 表,对多个列有唯一约束,其中一列可以为 NULL。我只希望每个组合都允许该列中的一条记录为 NULL。
create table my_table (
col1 int generated by default as identity primary key,
col2 int not null,
col3 real,
col4 int,
constraint ux_my_table_unique unique (col2, col3)
);
Run Code Online (Sandbox Code Playgroud)
我有一个 upsert 查询,当它遇到 col2、col3 中具有相同值的记录时,我想更新 col4:
insert into my_table (col2, col3, col4) values (p_col2, p_col3, p_col4)
on conflict (col2, col3) do update set col4=excluded.col4;
Run Code Online (Sandbox Code Playgroud)
但当 col3 为 NULL 时,冲突不会触发。我读过有关使用触发器的内容。请问,引发冲突的最佳解决方案是什么?
如果您可以找到一个永远不能合法存在的值col3
(确保使用检查约束),您可以使用唯一索引:
CREATE UNIQUE INDEX ON my_table (
col2,
coalesce(col3, -1.0)
);
Run Code Online (Sandbox Code Playgroud)
并将其用于您的INSERT
:
INSERT INTO my_table (col2, col3, col4)
VALUES (p_col2, p_col3, p_col4)
ON CONFLICT (col2, coalesce(col3, -1.0))
DO UPDATE SET col4 = excluded.col4;
Run Code Online (Sandbox Code Playgroud)
...添加了该子句NULLS NOT DISTINCT
。您的案例现在可以开箱即用:
ALTER TABLE my_table\n DROP CONSTRAINT IF EXISTS ux_my_table_unique\n, ADD CONSTRAINT ux_my_table_unique UNIQUE NULLS NOT DISTINCT (col2, col3);\n\nINSERT INTO my_table (col2, col3, col4)\nVALUES (p_col2, p_col3, p_col4)\nON CONFLICT (col2, col3) DO UPDATE\nSET col4 = EXCLUDED.col4;\n
Run Code Online (Sandbox Code Playgroud)\n看:
\nNULL
值不被视为彼此相等,因此永远不会触发违规UNIQUE
。这意味着,您当前的表定义没有执行您所说的操作。已经可以有多行了(col2, col3) = (1, NULL)
。ON CONFLICT
在您当前的设置中永远不会触发col3 IS NULL
。
您可以UNIQUE
使用两个部分UNIQUE
索引强制执行约束,如下所述:
适用于您的案例:
\nCREATE UNIQUE INDEX my_table_col2_uni_idx ON my_table (col2)\nWHERE col3 IS NULL;\n\nCREATE UNIQUE INDEX my_table_col2_col3_uni_idx ON my_table (col2, col3)\nWHERE col3 IS NOT NULL;\n
Run Code Online (Sandbox Code Playgroud)\n但 ON CONFLICT ... DO UPDATE
只能基于单个 UNIQUE
索引或约束。只有ON CONFLICT DO NOTHING
变体才能起到“包罗万象”的作用。看:
看起来你想要的目前是不可能的,但是有一个......
\n两个部分UNIQUE
索引就位后,您可以根据 的输入值使用正确的语句col3
:
WITH input(col2, col3, col4) AS (\n VALUES\n (3, NULL::real, 5) -- \xe2\x91\xa0\n , (3, 4, 5)\n )\n, upsert1 AS (\n INSERT INTO my_table AS t(col2, col3, col4)\n SELECT * FROM input WHERE col3 IS NOT NULL\n ON CONFLICT (col2, col3) WHERE col3 IS NOT NULL -- matching index_predicate!\n DO UPDATE\n SET col4 = EXCLUDED.col4\n WHERE t.col4 IS DISTINCT FROM EXCLUDED.col4 -- \xe2\x91\xa1\n )\nINSERT INTO my_table AS t(col2, col3, col4)\nSELECT * FROM input WHERE col3 IS NULL\nON CONFLICT (col2) WHERE col3 IS NULL -- matching index_predicate!\nDO UPDATE SET col4 = EXCLUDED.col4\nWHERE t.col4 IS DISTINCT FROM EXCLUDED.col4; -- \xe2\x91\xa1\n
Run Code Online (Sandbox Code Playgroud)\ndb<>在这里摆弄
\n适用于任何情况。
\n甚至适用于任意组合NULL
和NOT NULL
值的多个输入行col3
。
\n并且甚至不会比普通语句花费更多,因为每一行只进入两个 UPSERT 之一。
这是其中之一“尤里卡!” 尽管困难重重,但一切都会点击的查询。:)
\n::real
\xe2\x91\xa0 请注意CTE 中的显式转换input
。这个相关答案解释了原因:
\xe2\x91\xa1 最后一个WHERE
子句是可选的,但强烈推荐。UPDATE
如果它实际上没有改变任何东西,那将是一种浪费。看:
归档时间: |
|
查看次数: |
5625 次 |
最近记录: |