array_length () пустого массива, возвращающего NULL - PullRequest
18 голосов
/ 08 августа 2011

Я разрабатываю некоторые хранимые процедуры в PL / pgSQL, и некоторые из них вызывают у меня некоторые проблемы. Спроки, которые я разрабатываю, получают по параметру массив, который я использую в цикле FOR для получения всех его элементов. Для определения верхней границы FOR LOOP я использую функцию array_length.

FOR i IN 1..array_length(array,1) LOOP

   --array[i] something in here

END LOOP;

Проблемы возникают, когда я даю sprocs пустой массив. Вместо того, чтобы не входить в цикл, sproc просто возвращает ошибку, заявляя, что верхняя граница FOR LOOP равна NULL. Разве это не должно быть 0 ?

Что-то не так с FOR LOOP?

Есть ли другой способ использовать те же границы в LOOP без возврата NULL при использовании пустого массива?

Примечание : я знаю, что всегда могу использовать условие перед циклом, например:

IF array_length(array,1) IS NOT NULL THEN

но проблема в том, что этот sproc должен обрабатывать тысячи вызовов в кратчайшие сроки. Как так, я не смотрю на то, что добавляет ненужные накладные расходы на обработку. Я просто смотрю, есть ли какой-нибудь способ «циклически» пустого массива в LOOP.

Ответы [ 2 ]

14 голосов
/ 08 августа 2011

Как всегда, если вы хотите иметь другое поведение для значений NULL, используйте конструкцию coalesce:

FOR i IN 1..coalesce(array_length(array, 1), 0) LOOP
    RAISE NOTICE '%', array[i];
END LOOP;

Что касается возвращаемого значения: array_length(x, N) возвращает количество элементов в Nth измерение.Поскольку пустой массив не имеет измерений, он возвращает NULL.Вы правы, что это нелогично, если вы рассматриваете только простые массивы, но имеет смысл для многомерных массивов.

Редактировать: Как писал Эрвин Брандштеттер в комментариях, правильнее использоватьarray_lower / upper для циклического перебора индексов массива.Они будут работать для массивов, которые не основаны на 1.Они также принимают аргумент измерения и требуют объединения:

FOR i IN coalesce(array_lower(array, 1), 1)..coalesce(array_upper(array, 1), 1) LOOP
    RAISE NOTICE '%', array[i];
END LOOP;
4 голосов
/ 05 августа 2013

Чтобы избежать этой проблемы, выполните цикл по массиву с FOREACH, представленным в Postgres 9.1 :

FOREACH i IN ARRAY $1
LOOP
   -- do something
END LOOP;

В зависимости отв том, что вы хотите делать внутри цикла, вы можете вообще избежать цикла и использовать обычный SQL вместо unnest().Операции на основе множеств обычно выполняются быстрее, чем зацикливание в PostgreSQL.

Пример:

RETURN QUERY
SELECT elem || 'foo'
FROM unnest($1) AS t(elem);
...