5

I'm trying to update multiple rows using postgres, I'm using this code:

UPDATE foobar SET column_a = CASE
  WHEN column_b = '123' THEN 1
  WHEN column_b = '345' THEN 2
END;

If I create a new table this works flawlessly, but when running this on a large table with 8 million rows this hangs indefinitely. I've tried first in Admineer (web interface) and also in console.

However this works just fine:

UPDATE foobar SET column_a=1 WHERE column_b='123';

I'm hesitant to implement this approach into my code since I'll have thousands of updates at once and would prefer to put them in one statement. Any ideas as to why the first example would hang postgres and the second would work just fine? I just double checked and I have no rules applied on the table.

4

2 回答 2

6

问题是..

该声明:

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_aandcolumn_binteger. 在这种情况下,您问题中的单引号'123'从来没有帮助。您最好使用数字文字而不是字符串文字。(即使它也适用于字符串文字。)

类似'123'默认unknown类型的字符串文字。如果数字太大,
数字文字123 默认为integer- 或bigint/ 。numeric

如果您正在处理非默认数据类型,则必须显式转换。看起来像:

...
FROM  (
   VALUES
     ('123'::sometype, '1'::sometype)  -- first row defines row type
    ,('345', '2')
   ) val(b, a)
...
于 2013-09-14T14:20:12.560 回答
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')
于 2013-09-14T14:05:33.043 回答