当前使用 PostgreSQL 16.4。我查看了这篇文章,但它没有涵盖我的用例。
我想有条件地更新具有相同条件的两个(或更多)列,因此我不必重写它。这是一个非常简单的例子,当然我的情况比
TRUE
复杂很多。
create table test (a INT, b INT);
我可以这样做,但条件重复了。
update test SET a = CASE WHEN TRUE THEN 1 END, b = CASE WHEN TRUE THEN 2 END;
理想情况下我会这样做,但我收到错误:
update test SET (a, b) = (CASE WHEN TRUE THEN (1, 2) END);
ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
如果我确实更改为子查询,我也会收到错误:
update test SET (a, b) = (SELECT CASE WHEN TRUE THEN (1, 2) END);
ERROR: number of columns does not match number of values
如何编写子查询以便更新将 select 的输出视为 2 个值?如果我单独运行这个 SELECT ,我确实会得到 2 个值,所以我觉得我在某处缺少一些显式转换。
SELECT CASE WHEN TRUE THEN (1, 2) END
产生 1,2
类型的
RECORD
创建并填充
test
:
DROP TABLE IF EXISTS test;
CREATE TABLE test (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
a INT,
b INT
);
INSERT INTO
test (a, b)
VALUES
(0, 1),
(11, 12);
SELECT *
FROM test
ORDER BY id;
test
的初始内容为:
id | a | b |
---|---|---|
1 | 0 | 1 |
2 | 11 | 12 |
最简单的情况是要求在满足条件时分配值,否则将列设置为
NULL
。
如果 a
是偶数,以下命令将 1 分配给 b
,将 2 分配给 a
:
UPDATE test
SET (a, b) = (SELECT 1, 2 WHERE a % 2 = 0);
test
的内容为:
id | a | b |
---|---|---|
1 | 1 | 2 |
2 | 空 | 空 |
当要求根据条件的评估分配替代值时,会出现更复杂的情况。下一个命令演示了处理这种情况的方法。将
test
恢复到其初始内容后,运行以下命令,当 a
为偶数时交换 b
和 a
,否则增加 a
和 b
:
UPDATE test
SET
(a, b) = (
SELECT
a,
b
FROM (SELECT a % 2 = 0 AS met) c
CROSS JOIN LATERAL (SELECT b AS a, a AS b
WHERE c.met
UNION ALL
SELECT a + 1 AS a, b + 1 AS b
WHERE NOT c.met) n);
test
的内容将是:
id | a | b |
---|---|---|
1 | 1 | 0 |
2 | 12 | 13 |
捕获条件表达式的计算结果消除了重复表达式的需要,这使得代码更易于维护且更高效。 使用
UNION ALL
是因为保证两个子查询中只有一个会返回一行。