2016-06-10 3 views
1

Я пытаюсь запустить функцию plpgsql в многомерном столбце массива (int[][]) в выбранном запросе.Функция plpgsql, принимающая многомерный массив как параметр

Функция как таковой:

CREATE OR REPLACE FUNCTION public.reduce_to_percentages(int[][]) 
RETURNS float[] AS 
$function$ 
DECLARE 
    s int[]; 
    a float[]; 
BEGIN 
    FOREACH s SLICE 1 IN ARRAY $1 LOOP 
     a := a || s[2]::float/s[1]::float; 
    END LOOP; 
    RETURN a; 
END; 
$function$ 
LANGUAGE plpgsql VOLATILE; 

Следующий запрос работает:

SELECT reduce_to_percentages(ARRAY[ARRAY[100, 20], ARRAY[300, 50]]); 

Так делает следующий запрос:

SELECT reduce_to_percentages((SELECT counts FROM objects LIMIT 1)); 

Но следующий запрос предоставляет функцию с значение null и вызывает исключение при попытке.над $1:

SELECT reduce_to_percentages(counts) FROM objects; 

ответ

2

Вы можете исправить ошибку, проверив для NULL значения like @klin provided.
Но это накладывает помаду на свинью. Перепишите эту функцию, чтобы заменить процедурный цикл на установочное решение.Как правило, быстрее (особенно при использовании в контексте внешнего запроса), проще и автоматически Нуль-сейф:

CREATE OR REPLACE FUNCTION public.reduce_to_percentages(int[]) 
    RETURNS float[] AS 
$func$ 
SELECT ARRAY (SELECT $1[d][2]::float/$1[d][1] 
       FROM generate_subscripts($1,1) d) 
$func$ LANGUAGE sql IMMUTABLE; 

Похожие:

Примечания:

  • Литейное производство дивиденд до float. float/integer возвращает float автоматически.

  • Нет данных. int[][]. Просто int[]. Postgres позволяет обозначать, но игнорирует дополнительные размеры массива для типов массивов, так как сам тип данных одинаковый для всех измерений. Реквизиты:

  • Сделать функцию IMMUTABLE (так как она является) для повышения производительности, позволяющие индексировать или функцию-встраивание. Связанный:


Альтернативно, вы можете объявить функцию STRICT (синоним: RETURNS NULL ON NULL INPUT). Малая разница: он возвращает NULL для ввода NULL, а не пустой массив ('{}'), как указано выше.

CREATE OR REPLACE FUNCTION public.reduce_to_percentages(int[][]) 
    RETURNS float[] AS 
$function$ 
... 
$function$ LANGUAGE plpgsql IMMUTABLE STRICT;

The manual:

RETURNS NULL ON NULL INPUT или STRICT указывает, что функция всегда возвращает нуль всякий раз, когда какой-либо из его аргументов равны нулю. Если указан этот параметр , функция не выполняется, если есть нулевые аргументы ; вместо этого нулевой результат принимается автоматически.

Но простая функция SQL выше предпочтительна по нескольким причинам.

+0

Ничего себе, очень подробное объяснение, большое спасибо !!! Что касается 'int [] []' игнорируется, я знал это, но для своей собственной способности чтения я написал функцию, чтобы она была более явной в типе принимаемого значения. Две вещи, которые я до сих пор не понимаю: 1) в чем разница между неизменяемыми и неустойчивыми функциями, что делает эту функцию неизменной? 2) почему ответ @ klin решил мою проблему? Он добавил только случай для нулевых значений, который возвращает пустой список, но с помощью его функции мой запрос работал (для ненулевых значений). –

+0

@YotamOfek: Ad 1.) [Я добавил ссылку на большее объяснение] (http://stackoverflow.com/questions/28569415/how-do-immutable-stable-and-volatile-keywords-effect-behaviour-of- функция/28573737 # 28573737). Объявление 2.) Ошибка, которую вы видели, была поднята конструкцией 'FOREACH', которая не работает для ввода NULL. –

+0

* Положите помаду на свиньи * - у вас есть особое чувство юмора, но мне оно нравится. Вы знаете, что ваше решение быстрее или просто догадывается? – klin

2

В теле функции просто проверить, если аргумент не является нулевым. Если вы хотите вернуть пустой массив для нулевого аргумента, добавьте начальное значение для переменной a в разделе объявления (иначе функция вернет значение null).

CREATE OR REPLACE FUNCTION public.reduce_to_percentages(int[][]) 
RETURNS float[] AS 
$function$ 
DECLARE 
    s int[]; 
    a float[] = '{}';  -- initial value 
BEGIN 
    IF $1 IS NOT NULL THEN 
     FOREACH s SLICE 1 IN ARRAY $1 LOOP 
      a := a || s[2]::float/s[1]::float; 
     END LOOP; 
    END IF; 
    RETURN a; 
END; 
$function$ 
LANGUAGE plpgsql VOLATILE; 

Я сделал простой тест. Это скромная установка:

create table test (val int[]); 
insert into test 
select (array[array[ri(100), ri(100)], array[ri(100), ri(100)]]) 
from generate_series(1, 1000000); 

ri(100) моя функция, возвращающая случайное число между 1-100. Итак, таблица содержит миллион строк с массивом из двух массивов из двух целых чисел в одном столбце. Я попробовал тест как простой и типичный как можно скорее.

Следующий запрос был выполнен в 10 раз с функцией Эрвина и 10 раз с моим вариантом:

select sum(v[1]), sum(v[2]) 
from (
    select reduce_to_percentages(val) v 
    from test 
    ) s; 

Среднее время выполнения десяти тестов:

  • Эрвина функция 11940 мс
  • Клинский х функция 4750 мс

Возможно, моя функция - свинья с l ipstick на его рыло, но это быстрый.

+0

Благодарим вас за ответ, но значение столбца не равно null и определено как таковое. Вот почему работает подзапрос. –

+0

Нет, подзапрос работает, потому что у вас есть «limit 1» (и выбранное значение не равно null). Просто попробуйте функцию. – klin

+0

Это работает! Не могли бы вы объяснить, почему? Я имею в виду, насколько я вижу, что вы изменили в функции, вы просто возвращаете пустой массив в случае нулевого значения. Как это работает, в отличие от проверки нуля? Благодаря!! –

Смежные вопросы