Развернуть массив на один уровень

Я хочу взять массив 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 ответа

Решение

объяснять

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}}

Чтобы сгладить результат (получить одномерный массив):

Читайте больше в руководстве здесь.

функция

Более поздний тест показал, что эта функция 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, 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.

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

обновление: изменено для использования встроенной агрегатной функции 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)

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

Другие вопросы по тегам