尝试使用CASE语句更新多行时,Postgres会挂在大表上

new*_*ere 4 sql postgresql case sql-update

我正在尝试使用postgres更新多行,我正在使用此代码:

UPDATE foobar SET column_a = CASE
  WHEN column_b = '123' THEN 1
  WHEN column_b = '345' THEN 2
END;
Run Code Online (Sandbox Code Playgroud)

如果我创建一个新表,它可以完美地运行,但是当在一个有800万行的大表上运行它时,它会无限期地挂起.我先在Admineer(Web界面)和控制台中尝试过.

但是这很好用:

UPDATE foobar SET column_a=1 WHERE column_b='123';
Run Code Online (Sandbox Code Playgroud)

我对在我的代码中实现这种方法犹豫不决,因为我将同时拥有数千个更新,并且更愿意将它们放在一个语句中.关于为什么第一个例子会挂起postgres的任何想法,第二个例子可以正常工作?我只是双重检查,我没有在桌子上应用规则.

Erw*_*ter 5

问题是......

该声明:

CASE
  WHEN column_b = '123' THEN 1
  WHEN column_b = '345' THEN 2
END;
Run Code Online (Sandbox Code Playgroud)

..只是简称:

CASE
  WHEN column_b = '123' THEN 1
  WHEN column_b = '345' THEN 2
  ELSE NULL
END
Run Code Online (Sandbox Code Playgroud)

意思是,如果没有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
Run Code Online (Sandbox Code Playgroud)

加入一组 beatsCASE轻松迭代每一行的一长串分支.随着更长的列表,差异将迅速增长.

此外,请确保在任何一种方式都有索引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)
...
Run Code Online (Sandbox Code Playgroud)