Перебрать параметр массива БЕЗ foreach

CREATE OR REPLACE FUNCTION fnMyFunction(recipients recipient[]) ...

    FOREACH v_recipient IN ARRAY recipients
       LOOP
          v_total := v_total + v_recipient.amount;
          INSERT INTO tmp_recipients(id, amount)
          VALUES(v_recipient.id, v_recipient.amount::numeric(10,2));
    END LOOP;

...

Это прекрасно работает в среде разработки, но только что выяснилось, что среда выпуска — 8.4, которая, похоже, не поддерживает конструкцию FOREACH. Я надеялся, что кто-то может пролить свет на альтернативную реализацию цикла for, используя набор параметров массива и используя значения из массива аналогичным образом, чтобы избежать полного рефакторинга.

Сообщение об ошибке, которое я получаю:

ОШИБКА: синтаксическая ошибка в состоянии SQL "FOREACH" или рядом с ним: 42601 Контекст: оператор SQL в функции PL/PgSQL "fnMyFunction" рядом со строкой ##

Среда db находится на общем хосте, поэтому у меня нет вариантов обновления платформы.
Я пометил postgres 9.1 и 8.4, потому что эта функция правильно работает в 9.x, но не работает в 8.4.


person Ryan Fisch    schedule 02.10.2012    source источник


Ответы (2)


Используйте unnest; Кажется, это было в 8.4. Не проверено, но я думаю, что это правильно:

FOR v_recipient IN SELECT vr FROM unnest(recipients) x(vr)
LOOP
....
END LOOP;

Если вы не можете этого сделать, вам придется перебрать array_length, используя индексацию в массиве.

person Craig Ringer    schedule 02.10.2012
comment
Так что похоже, что это работает, к сожалению, я не нахожу четкого объяснения unnest. Теперь проблема в том, что я, кажется, получаю первый элемент в элементе v_recipient, но не второй. Элемент получателя основан на идентификаторе двух полей и сумме, я предполагаю, что структура будет выглядеть примерно так: {{1,1.00},{2,1.00}} - person Ryan Fisch; 02.10.2012

Как у вас есть, вы выполняете один INSERT за раз. В реляционных базах данных операции над наборами обычно намного быстрее, чем итерация по записям по одной.

Это должно быть проще, быстрее и работать с PostgreSQL 8.4 или более поздней версии:

INSERT INTO tmp_recipients(id, amount)
SELECT (r.col).*
FROM   (SELECT unnest(recipients) AS col) r

Это предполагает, что составной базовый тип массива состоит из (id, amount) — именно в таком порядке — и тип amount может быть приведен к numeric(10,2). В противном случае, или просто чтобы быть уверенным, будьте более явными:

INSERT INTO tmp_recipients(id, amount)
SELECT (r.col).id, (r.col).amount::numeric(10,2)
FROM   (SELECT unnest(recipients) AS col) r

Скобки вокруг (r.col) необязательны. Они необходимы для устранения неоднозначности синтаксиса составного типа.

person Erwin Brandstetter    schedule 21.10.2012