假设我要进行批量更新,为 a 值的集合设置 a = b 。这可以通过一系列UPDATE
查询轻松完成:
UPDATE foo SET value='foo' WHERE id=1
UPDATE foo SET value='bar' WHERE id=2
UPDATE foo SET value='baz' WHERE id=3
但现在我想我想要批量做这件事。我有一个包含id和新值的二维数组:
[ [ 1, 'foo' ]
[ 2, 'bar' ]
[ 3, 'baz' ] ]
是否有一种有效的方法可以在单个SQL查询中执行这三个UPDATE?
我考虑过的一些解决方案:
临时表
CREATE TABLE temp ...;
INSERT INTO temp (id,value) VALUES (....);
UPDATE foo USING temp ...
但这确实只是解决了问题。尽管进行批量INSERT可能更容易(或至少不那么难看),但仍然至少有三个查询。
通过将数据对作为SQL数组传递来对输入进行非规范化。这使查询非常难看,但
UPDATE foo
USING (
SELECT
split_part(x,',',1)::INT AS id,
split_part(x,',',2)::VARCHAR AS value
FROM (
SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) AS x
) AS x;
)
SET value=x.value WHERE id=x.id
这使得可以使用单个查询,但会使查询变得丑陋且效率低下(特别是对于混合和/或复杂数据类型)。
有更好的解决方案吗?或者我应该求助于多个UPDATE查询?
答案 0 :(得分:5)
通常,您希望从具有足够索引的table
批量更新,以便轻松进行合并:
CREATE TEMP TABLE updates_table
( id integer not null primary key
, val varchar
);
INSERT into updates_table(id, val) VALUES
( 1, 'foo' ) ,( 2, 'bar' ) ,( 3, 'baz' )
;
UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
;
所以你应该通过以下方式填充你的update_table:
INSERT into updates_table(id, val)
SELECT
split_part(x,',',1)::INT AS id,
split_part(x,',',2)::VARCHAR AS value
FROM (
SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz'])
) AS x
;
请记住:id
中updates_table
字段上的索引(或主键)非常重要。 (但对于像这样的小集合,可能由优化器选择一个hashjoin)
此外:对于更新,重要的是避免使用相同的值进行更新,这些会导致在提交更新后创建额外的rowversions +以及生成的VACUUM
活动:
UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
AND (t.value IS NULL OR t.value <> u.value)
;