我正在尝试使用postgres更新多行,我正在使用此代码:
UPDATE foobar SET column_a = CASE
WHEN column_b = '123' THEN 1
WHEN column_b = '345' THEN 2
END;
如果我创建一个新表,这可以完美地工作,但是当在一个有800万行的大表上运行时,这会无限期地挂起。我先在Admineer(Web界面)和控制台中尝试过。
然而这很好用:
UPDATE foobar SET column_a=1 WHERE column_b='123';
我对在我的代码中实现这种方法犹豫不决,因为我将同时拥有数千个更新,并且更愿意将它们放在一个语句中。关于为什么第一个例子会挂起postgres的任何想法,第二个例子可以正常工作?我只是仔细检查了一下,我没有在桌子上应用任何规则。
答案 0 :(得分:5)
声明:
CASE
WHEN column_b = '123' THEN 1
WHEN column_b = '345' THEN 2
END;
..只是简称:
CASE
WHEN column_b = '123' THEN 1
WHEN column_b = '345' THEN 2
ELSE NULL
END
意思是,如果没有WHERE
子句,您的UPDATE
语句不只是“尝试”,它实际上会更新表格中的每一行,其中大多数都是NULL
。
也许,列上的NOT NULL
约束阻止了数据丢失......
大集的我会同时拥有数千个更新,并希望将它们放在一个声明中。
快得多 (和更短):
UPDATE foobar f
SET column_a = val.a
FROM (
VALUES
(123, 1)
,(345, 2)
) val(b, a)
WHERE f.column_b = val.b
加入一个 ,可以轻松地遍历每一行CASE
个分支的长列表。随着更长的列表,差异将迅速增长。
此外,请务必在column_b
上以任意方式获得索引。
您可以将VALUES
表达式替换为任何表,视图或子选择,从而产生适当的行。
注意:
我假设column_a
和column_b
属于integer
类型。在这种情况下,问题中'123'
周围的单引号从不会有用。您最好使用数字文字而不是字符串文字。 (即使它也适用于字符串文字。)
'123'
之类的字符串文字默认为unknown
类型
如果数字太大,则123
等数字文字默认为integer
- 或bigint
/ numeric
。
如果您正在处理非默认数据类型,则必须显式转换。看起来像是:
...
FROM (
VALUES
('123'::sometype, '1'::sometype) -- first row defines row type
,('345', '2')
) val(b, a)
...
答案 1 :(得分:2)
如果有人遇到这个问题,我会保留这个问题。
这个查询是罪魁祸首:
UPDATE foobar SET column_a = CASE
WHEN column_b = '123' THEN 1
WHEN column_b = '345' THEN 2
END;
问题是它缺少一个WHERE语句,所以它试图更新所有行。对于大型数据库,这可能是一个问题,在我的情况下,它只是超时。一旦我在那里添加了where语句就修复了这个问题。
以下是解决方案:
UPDATE foobar SET column_a = CASE
WHEN column_b = '123' THEN 1
WHEN column_b = '345' THEN 2
END
WHERE column_b IN ('123','345')