Развернуть массив на один уровень - PullRequest
9 голосов
/ 15 ноября 2011

Я хочу взять массив n измерений и вернуть набор, содержащий строки массивов n-1 измерений.Например, возьмите массив ARRAY[[1,2,3], [4,5,6], [7,8,9]] и верните набор {1,2,3}, {4,5,6}, {7,8,9}.Использование unnest возвращает набор 1,2,3,4,5,6,7,8,9.

. Я попытался получить функцию unnest из PostgreSQL 8.4, которая, похоже, будет выполнять то, что я ищу:

CREATE OR REPLACE FUNCTION tstng.unnest2(anyarray)
    RETURNS SETOF anyelement
    LANGUAGE plpgsql
    IMMUTABLE
    AS $$
    BEGIN
            RETURN QUERY SELECT $1[i]
                FROM generate_series(array_lower($1,1), array_upper($1,1)) i;
        END;
    $$;

Однако, SELECT tstng.unnest2(ARRAY[[1,2,3], [4,5,6], [7,8,9]]); возвращает набор , , (т. Е. 3 пустых строки).

Я также обнаружил, что SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[0]; возвращает нулевое значение, которое, как я считаю, является корнем моей проблемы.

Ответы [ 3 ]

13 голосов
/ 16 ноября 2011

Объяснять

SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[0]

возвращает так же, как

SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[17]

, который равен NULL. Я цитирую документов по этому вопросу :

По умолчанию значение индекса нижней границы измерений массива установить на один.

0 здесь не имеет особого значения. Кроме того, для двумерных массивов вам нужно два индекса , чтобы получить базовый элемент. Как это:

SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1][2]

Результат:

2

Первая часть вашего сообщения немного неясна.

SELECT array_dims(ARRAY[[1,2,3], [4,5,6], [7,8,9]])

Результат:

[1:3][1:3]

Это два размера с 3 элементами (от 1 до 3) каждый (9 базовых элементов).
Если вы хотите n-1 размеры, то это правильный результат:

SELECT ARRAY (SELECT unnest('{{1,2,3}, {4,5,6}, {7,8,9}}'::int[]))

Результат:

{1,2,3,4,5,6,7,8,9}

Это один размер. unnest() всегда производит один базовый элемент на строку. Я не уверен, какой именно результат вы хотите. Ваш пример - просто еще один двумерный массив с отсутствующим набором фигурных скобок ...?

{1,2,3}, {4,5,6}, {7,8,9}

Если вы хотите срез массива , попробуйте эту запись:

SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1:2]

Результат:

{{1,2,3},{4,5,6}}

Или это:

SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[2:2][1:2]

Результат:

{{4,5}}

To Свести Результат (получить массив 1D):

Подробнее в руководстве здесь .

Функция

Более поздний тест показал, что эта функция plpgsql работает на намного быстрее. Требуется Postgres 9.1 или более поздняя версия:

CREATE OR REPLACE FUNCTION unnest_2d_1d(ANYARRAY, OUT a ANYARRAY)
  RETURNS SETOF ANYARRAY AS
$func$
BEGIN
   FOREACH a SLICE 1 IN ARRAY $1 LOOP
      RETURN NEXT;
   END LOOP;
END
$func$  LANGUAGE plpgsql IMMUTABLE;

См:

Это улучшенная и упрощенная версия функции, которую Лукас разместил :

CREATE OR REPLACE FUNCTION unnest_2d_1d(anyarray)
  RETURNS SETOF anyarray AS
$func$
SELECT array_agg($1[d1][d2])
FROM   generate_subscripts($1,1) d1
    ,  generate_subscripts($1,2) d2
GROUP  BY d1
ORDER  BY d1
$func$  LANGUAGE sql IMMUTABLE;

Для Postgres версии <8.4, <code>array_agg() не устанавливается по умолчанию. Сначала создайте его:

CREATE AGGREGATE array_agg(anyelement) (
 SFUNC=array_append,
 STYPE=anyarray,
 INITCOND='{}'
);

Также generate_subscripts() еще не родился. Используйте вместо:

...
FROM   generate_series(array_lower($1,1), array_upper($1,1)) d1
    ,  generate_series(array_lower($1,2), array_upper($1,2)) d2
...

Звоните:

SELECT unnest_2d_1d(ARRAY[[1,2], [3,4], [5,6]]);

Результат

{1,2}
{3,4}
{5,6}

SQL Fiddle.

7 голосов
/ 15 марта 2012

Срезы многомерного возвращаются в виде многомерных массивов.Это модифицированная версия unnest, которая принимает 2-мерный массив и возвращает набор 1-мерных массивов.

update : изменено для использования встроенной функции агрегата array_agg, которая была по умолчанию на 8.4.(http://www.postgresql.org/docs/9.2/static/functions-aggregate.html)

Предостережения:

  • Это работает только для 2-мерных массивов (вероятно, я должен переименовать функцию, чтобы отразить это ограничение).
  • Если вы используете 8.3 (и не можете обновить), вам нужно определить агрегат array_accum и изменить все ссылки в функциях ниже с array_agg на array_accum. http://www.postgresql.org/docs/8.3/static/xaggr.html

код:

CREATE OR REPLACE FUNCTION unnest_multidim(anyarray)
RETURNS SETOF anyarray AS
$BODY$
  SELECT array_agg($1[series2.i][series2.x]) FROM
    (SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i
     FROM 
     (SELECT generate_series(array_lower($1,1),array_upper($1,1)) as i) series1 
    ) series2
GROUP BY series2.i
$BODY$
LANGUAGE sql IMMUTABLE;

Результат:

select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]]);
unnest_multidim
----------------------
{1,2,3}
{4,5,6}
{7,8,9}
(3 rows)

Теперь, скажем, по какой-то причине вам нужен простой доступ только квозвращается один из этих массивов. Следующая функция добавляет необязательный параметр index, который будет возвращать вложенный массив предоставленного вами индекса, или, если вы укажете null, выведет полный набор «неопубликованных» массивов.

CREATE OR REPLACE FUNCTION unnest_multidim(anyarray, integer)
  RETURNS SETOF anyarray AS
$BODY$
SELECT array_agg($1[series2.i][series2.x]) FROM
  (SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i
  FROM 
    (SELECT CASE WHEN $2  IS NULL THEN
      generate_series(array_lower($1,1),array_upper($1,1)) 
      ELSE $2
      END as i) series1
  ) series2
GROUP BY series2.i
$BODY$
LANGUAGE sql IMMUTABLE;

Результаты:

db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],2);
 unnest_multidim 
-----------------
 {4,5,6}
(1 row)

db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],NULL);
 unnest_multidim 
-----------------
 {1,2,3}
 {4,5,6}
 {7,8,9}
(3 rows)
0 голосов
/ 02 июля 2013

Предупреждение: при использовании array_agg на postgres PostgreSQL array_agg order Если вы планируете использовать необъявленный массив скажем для поиска argmax, это повредит ваши данные.

...