PostgresQL存储过程:在更新语句中使用一组结果而不循环遍历集合

时间:2012-06-19 17:47:32

标签: postgresql stored-procedures local-variables

我有一个存储过程,我需要在其中查询一组ids,然后在UPDATE语句的WHERE子句中使用该集合。我正在使用 PostgresQL 9.0 + 。我宁愿不在ids集上使用循环并发出多个UPDATE语句 - 这不是很有效。

这是一个简单的例子:

CREATE OR REPLACE FUNCTION test_it()
RETURNS VOID AS $$
DECLARE
  cur_time  TIMESTAMP;
  ids       a%ROWTYPE;
BEGIN
  SELECT id FROM a INTO ids;
  UPDATE b
     SET state = 'foobar', updated_at = cur_time
   WHERE id IN ids;
END;
$$ LANGUAGE plpgsql;

这甚至都没有编译。

我也尝试了 SELECT -ing ids就像这样......

CREATE OR REPLACE FUNCTION test_it()
RETURNS VOID AS $$
DECLARE
  cur_time  TIMESTAMP;
  ids       a%ROWTYPE;
BEGIN
  SELECT id FROM a INTO ids;
  UPDATE b
     SET state = 'foobar', updated_at = cur_time
   WHERE id IN (SELECT ids);
END;
$$ LANGUAGE plpgsql;

这会抛出一个错误:提示:没有运算符匹配给定的名称和参数类型。您可能需要添加显式类型转换。

我的实际存储过程要复杂得多,因为ids集的初始查询实际上是一个动态查询。

实际错误输出是这个(只是更多上下文......):

ERROR:  operator does not exist: integer = task_responses
LINE 3:      WHERE id IN (SELECT task_response_ids)
                      ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:  UPDATE task_responses
       SET state = state, updated_at = cur_time, rejected_at = cur_time
     WHERE id IN (SELECT task_response_ids)
CONTEXT:  PL/pgSQL function "reject_submissions_with_comment" line 38 at SQL statement

2 个答案:

答案 0 :(得分:1)

在执行我的SP期间,我需要循环结果,所以我选择跟踪我访问它们的ID,稍后在动态查询中使用它们来执行单个更新。

FWIW:我永远无法得到@ Steve的建议,因为task_response_ids是一个结果集,而不是表表达式。如果我嵌入了可行的查询,但在我的用例中我需要多次运行相同的查询,因为我有多个更新(不同的表)要做。

以下是基于我的需求的(假)代码,如上所述和原始问题:

CREATE OR REPLACE FUNCTION test_it() RETURNS VOID AS $$
DECLARE
  cur_time    TIMESTAMP;
  state       varchar(20);
  a_response  RECORD;
  ids         bigint[];
  other_ids   bigint[];
  s_ids       varchar(4000);
  s_other_ids varchar(4000);
BEGIN
  state    := 'foobar';
  cur_time := CURRENT_TIMESTAMP;

  FOR a_response IN (SELECT id,other_id FROM a) LOOP
    ids[id_index]       := a_response.id;
    other_ids[id_index] := a_response.other_id;
    id_index            := id_index + 1;
    -- do other stuff with the current record
  END LOOP;
  s_ids       := array_to_string(ids, ',');
  s_other_ids := array_to_string(other_ids, ',');

  EXECUTE '
    UPDATE b
       SET state = $1, updated_at = $2
     WHERE id IN (' || s_ids || ')'
  USING state, cur_time;

  EXECUTE '
    UPDATE c
       SET state = $1, updated_at = $2
     WHERE id IN (' || s_other_ids || ')'
  USING state, cur_time;

END;
$$ LANGUAGE plpgsql;

这段代码非常虚构,但它展示了我需要完成的事情。

答案 1 :(得分:0)

根据http://www.postgresql.org/docs/9.1/static/sql-update.html

,您需要UPDATE ... FROM ...样式查询
UPDATE b
  SET state = 'foobar', updated_at = cur_time
FROM
  ids i
WHERE
  b.id = i.id;