Подтвердить что ты не робот

Как применить функцию к каждому элементу столбца массива в Postgres?

Запрос Pg возвращает массив. Я хотел бы получить это с каждым элементом, отформатированным до 3 знаков после запятой. Как я могу применить функцию к каждому элементу массива? Что-то вроде следующего (неправильно, очевидно) -

SELECT Round(ARRAY[1.53224,0.23411234], 2);
{1.532, 0.234}

Думаю, я ищу что-то вроде функции Perl map.

4b9b3361

Ответ 1

Возможно, вам понадобится создать сохраненную функцию. Вот тот, который делает то, что вам нужно:

CREATE OR REPLACE FUNCTION array_round(float[], int)
RETURNS float[]
AS
$$
DECLARE
   arrFloats ALIAS FOR $1;
   roundParam ALIAS FOR $2;
   retVal float[];
BEGIN
   FOR I IN array_lower(arrFloats, 1)..array_upper(arrFloats, 1) LOOP
    retVal[I] := round(CAST(arrFloats[I] as numeric), roundParam);
   END LOOP;
RETURN retVal;
END;
$$
LANGUAGE plpgsql 
   STABLE 
RETURNS NULL ON NULL INPUT;

Затем вызовите что-то вроде этого:

# SELECT array_round(ARRAY[1.53224,0.23411234], 2);
 array_round 
-------------
 {1.53,0.23}
(1 row)

Ответ 2

Сначала включите массив в набор, используя disable:

> SELECT n FROM unnest(ARRAY[1.53224,0.23411234]) AS n;
     n      
------------
    1.53224
 0.23411234
(2 rows)

Затем примените выражение к столбцу:

> SELECT ROUND(n, 2) FROM unnest(ARRAY[1.53224,0.23411234]) AS n;
 round 
-------
  1.53
  0.23
(2 rows)

Наконец, используйте array_agg, чтобы вернуть набор в массив:

> SELECT array_agg(ROUND(n, 2)) FROM unnest(ARRAY[1.53224,0.23411234]) AS n;
  array_agg  
-------------
 {1.53,0.23}
(1 row)

Ответ 3

postgres=# select array(select round(unnest(array[1.2,2.4,3,4])));
   array   
-----------
{1,2,3,4}
(1 row)

Ответ 4

Вам нужно превратить массив в набор строк. Например, используя generate_series:

SELECT ARRAY(SELECT ROUND(ARRAY[1.53224,0.23411234])[i], 2) FROM generate_series(1,2) AS s(i));    

Я знаю это довольно уродливо. Для облегчения таких сопоставлений должна быть вспомогательная функция.

Возможно, что-то вроде (да, это ужасный, медленный и хрупкий динамический код):

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT)
RETURNS ANYARRAY
IMMUTABLE STRICT
LANGUAGE 'plpgsql' AS
$$
DECLARE
    i INTEGER;
    t TEXT;
    cmd TEXT;
BEGIN
    FOR i IN array_lower($2, 1) .. array_upper($2, 1) LOOP
        cmd := 'SELECT ('||quote_ident($1)||'('||quote_nullable($2[i])||', '||quote_nullable($3)||'))::TEXT';
        EXECUTE cmd INTO t;
        $2[i] := t;
    END LOOP;
    RETURN $2;
END;
$$;

select map_with_arg('repeat', array['can','to']::TEXT[], '2');
 map_with_arg
---------------
 {cancan,toto}

Обновление. Мне кажется, что мы можем использовать один динамический оператор для всего цикла. Это может уменьшить некоторые проблемы с производительностью.

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT)
RETURNS ANYARRAY
IMMUTABLE STRICT
LANGUAGE 'plpgsql' AS
$$
DECLARE
    cmd TEXT;
    rv TEXT;
BEGIN
    cmd := 'SELECT ARRAY(SELECT (' || quote_ident($1)||'($1[i], '||quote_nullable($3)||'))::TEXT FROM generate_subscripts($1, 1) AS gs(i))';
    EXECUTE cmd USING $2 INTO rv;
    RETURN rv;
END;
$$;