Выберите каждый первый элемент массива целочисленных массивов в массив

Как выбрать каждый первый элемент массива целочисленных массивов в массив?
{{1,2,3},{2,15,32},{5,16,14},...} -> {1,2,5,...}

3 ответа

Решение

Так как PostgreSQL позволит запрашивать срез за пределами размера массива, и при условии, что никогда не будет более 999 подмассивов, мы можем использовать это чудовище

WITH data AS (
  SELECT array[array[1,2,3], array[2,15,32], array[5,16,14]] as arr)
SELECT array_agg(arr)
  FROM (SELECT unnest(arr[1:999][1]) as arr from data) data2;

Вы можете, конечно, сделать константу 999 больше, если это необходимо, это просто случайное большое число, которое я добавил туда.

Причина, по которой это так сложно, заключается в том, что если бы вы использовали только arr[1:999][1] вы все равно получите двумерный массив, но только с первыми элементами. В этом случае {{1}, {2}, {5}}, Если мы используем unnest() мы можем сделать это в набор, который затем может быть подан в array_agg() через подвыбор.

Было бы неплохо использовать array_agg(unnest(arr[1:999][1])) но функция агрегации не любит множества, и я не знаю, есть ли способ конвертировать ее на лету.

Вы также можете использовать фактическую длину массива, но это может привести к ненужным вычислениям

SELECT unnest(arr[1:array_length(arr, 1)][1]) as arr from data

Заметка

Если массивы могут быть неопубликованы одним уровнем, вы можете просто проиндексировать массивы, а затем использовать array_agg() преобразовать его обратно в массив с гораздо более простым синтаксисом

WITH data AS
  (SELECT array[1,2,3] as arr
   UNION ALL SELECT array[2,15,32] as arr
   UNION ALL SELECT array[5,16,14] as arr)
SELECT array_agg(arr[1]) from data;

CTE там только для входных данных, фактическое мясо является array_agg(arr[1]), Это, конечно, будет работать для любого количества входных массивов.

Учитывая эту таблицу:

CREATE TEMP TABLE arrtbl (
   arrtbl_id serial PRIMARY KEY
 , arr int[]
);

Примерные значения:

INSERT INTO arrtbl (arr)
VALUES 
  ('{{1,2,3},{2,15,32},{5,16,14}}')
, ('{{17,22},{1,15},{16,14}}')   -- dimensions can vary across rows!
, ('{}')
, (null);

Это сделало бы работу для всех строк:

SELECT arrtbl_id, array_agg(a) AS a1
FROM   arrtbl t
    ,  unnest(t.arr[-2147483648:2147483647][1]) a
GROUP  BY 1;

Зачем [-2147483648:2147483647]?

Результат:

arrtbl_id | a1
----------+-----------
1         | '{1,2,5}'
2         | '{17,1,16}'

Обратите внимание, что строки с пустым массивом / NULL в arr исключены из результата.
Кроме того, хотя вышеуказанное обычно работает, лучше использовать этот безопасный синтаксис:

SELECT arrtbl_id, array_agg(a.a ORDER BY a.ordinality)
FROM   arrtbl t
LEFT   JOIN LATERAL unnest(t.arr[-2147483648:2147483647][1]) WITH ORDINALITY a ON true
GROUP  BY 1;

Результат:

arrtbl_id | a1
----------+-----------
1         | '{1,2,5}'
2         | '{17,1,16}'
3         | null
4         | null

Детальное объяснение:

Plpgsql решение:

create or replace function first_elements(arr anyarray)
returns int[] language plpgsql
as $$
declare
    i int;
    res int[];
begin
    for i in 1..array_length(arr, 1) loop
        res = array_append(res, arr[i][1]);
    end loop;
    return res;
end $$;  

with test as (
    select array[array[1,2,3], array[2,15,32], array[5,16,14]] a
    union
    select array[array[101,0], array[102,0]] a
    )
select first_elements(a) from test;

 first_elements
----------------
 {1,2,5}
 {101,102}
(2 rows)    
Другие вопросы по тегам