【发布时间】:2019-11-21 16:50:11
【问题描述】:
我有一张表,其中每组只有一行可以为真。 这是由部分唯一索引(不能延迟)强制执行的。
CREATE TABLE test
(
id SERIAL PRIMARY KEY,
my_group INTEGER,
last BOOLEAN DEFAULT TRUE
);
CREATE UNIQUE INDEX "test.last" ON test (my_group) WHERE last;
INSERT INTO test (my_group)
VALUES (1), (2);
我正在尝试在此表中插入一个新行,该行将替换相应组的“最后一个”元素。
我也想在一个语句中完成。
通过一些 CTE 技巧,我可以做到这一点:link to Fiddle
-- the statement is structured this way to closely resemble my actual usecase
WITH
new_data AS (
VALUES (1)
),
uncheck_old_last AS (
UPDATE test
SET last = FALSE
WHERE last AND my_group in (SELECT * FROM new_data)
RETURNING TRUE
)
INSERT INTO test (my_group)
SELECT *
FROM new_data
WHERE COALESCE((SELECT * FROM uncheck_old_last LIMIT 1), true);
到目前为止一切顺利,插入发生...没有冲突。
我不太明白为什么这是有效的,因为据我了解,所有 CTE 都应该读取相同的初始数据库状态并且不能查看其他 CTE 所做的更改
现在的问题是,当我尝试一次对多行执行相同操作时,我遇到了唯一的违规:Link to Fiddle
-- the statement is structured this way to closely resemble my actual usecase
WITH
new_data AS (
VALUES (1), (2) -- <- difference to above query
),
uncheck_old_last AS (
UPDATE test
SET last = FALSE
WHERE last AND my_group in (SELECT * FROM new_data)
RETURNING TRUE
)
INSERT INTO test (my_group)
SELECT *
FROM new_data
WHERE COALESCE((SELECT * FROM uncheck_old_last LIMIT 1), true);
-- Schema Error: error: duplicate key value violates unique constraint "test.last"
有没有办法用一个语句插入多行/
有人可以向我解释为什么第一个查询有效而第二个查询无效吗?
【问题讨论】:
-
我会使用 BEFORE INSERT 触发器来解决这个问题,这意味着您可以使用标准的 INSERT 语句而不必担心。
-
触发器可能会更干净,是的。只是我们在我正在处理的项目中不做触发器(我想保持所有查询字符串自包含并使维护更容易。现在我也总是更喜欢 cte 解决方案,因为它更容易基于优化在查询计划上,使用触发器我总是必须跟踪查询实际执行的代码)。但是我会问项目负责人是否会考虑为此使用触发器,也许这在我所知道的更多地方使用并且触发器实际上是有意义的。
-
好吧,你可以简单地将两个语句(
UPDATE然后INSERT)放入一个事务中,然后你就不需要对抗 CTE 的语义 -
哈哈好吧,你看...我使用的包装器没有公开 pdo 事务功能。我已经创建了一个拉取请求来添加它们,但目前我们不太可能使用它,项目负责人没有时间更新服务器的 docker 映像。但感谢您的努力。
-
Postgres 允许在单个“go”中发送多个语句
update test set ... where ...; insert into ...;
标签: postgresql common-table-expression unique-constraint unique-index