2015-02-10 2 views
2

Предположим, что у меня есть таблица вроде этого:Postgres - поиск мин массива

link_ids | length 
------------+----------- 
{1,4}  | {1,2} 
{2,5}  | {0,1} 

Как я могу найти длину мин для каждого link_ids?

Так что конечный результат будет выглядеть примерно так:

link_ids | length 
------------+----------- 
{1,4}  | 1 
{2,5}  | 0 
+0

Почему ваши столбцы массивы? Это выглядит очень плохой дизайн схемы. – Falmarri

+0

Чтобы увеличить комментарий от @Falmarri - существует набор формальных правил (называемых нормальными формами), которые описывают, как должна выглядеть схема реляционных баз данных, чтобы предотвратить множество проблем - считается разумным соответствовать по меньшей мере первые три - ваша схема не соответствует первой, так как ячейки вашей таблицы не хранят атомные значения. Вы должны использовать таблицы для хранения списков. – lared

+0

Я знаком с нормальными формами. Это результат рекурсивного запроса, который я хотел бы продолжить. – jaynp

ответ

6

Предполагая таблицу, как это:

CREATE TABLE t (
    link_ids int[] PRIMARY KEY  -- which is odd for a PK 
, length int[] 
, CHECK (length <> '{}'::int[]) -- rules out null and empty in length 
); 

Этот запрос работает в Postgres 9.3+

SELECT link_ids, min(len) As min_length 
FROM t, unnest(t.length) len -- implicit LATERAL join 
GROUP BY 1; 

Или создать маленькую функцию (Postgres 8.4+):

CREATE OR REPLACE FUNCTION arr_min(anyarray) RETURNS anyelement AS 
'SELECT min(i) FROM unnest($1) i' LANGUAGE sql IMMUTABLE; 

Тогда:

SELECT link_ids, arr_min(length) AS min_length FROM t; 

Или, чтобы сделать этот быстро, до тех пор, как мы имеем дело с integer массивами тривиального длины, можно использовать дополнительный модуль intarray и использование встроенный sort() function (Postgres 8.3+):

SELECT link_ids, (sort(length))[1] AS min_length FROM t; 
+0

Ницца! Это БОЛЬШОЕ СОЕДИНЕНИЕ помогает мне много. Избегайте большого количества подзапросов в моем случае. Благодаря. –

0

(я буду считать, link_ids может иметь двойников и поскольку нет столбца ID мы собираемся импровизировать).

WITH r AS 
(SELECT row_number() OVER() as id, 
     link_ids, 
     length from Table1) 
SELECT DISTINCT ON (id) link_ids, 
     unnest(length) 
FROM r 
ORDER BY id, length; 

fiddle

2

Предполагая, что имя таблицы t и каждое значение link_ids является уникальным.

select link_ids, min(len) 
from (select link_ids, unnest(length) as len from t) as t 
group by link_ids; 

link_ids | min 
----------+----- 
{2,5} | 0 
{1,4} | 1 
1

Небольшое дополнение к ответу Эрвина - иногда подзапрос с unnest может быть даже дешевле, чем боковое соединение.

Я использовал определение таблицы от ответа Эрвина и наполнил его:

t=# insert into t select '{1}'::int[]||g,'{1}'::int[]||g from generate_series(1,9999,1) g; 
INSERT 0 9999 
t=# select * from t order by ctid desc limit 1; 
link_ids | length 
----------+---------- 
{1,9999} | {1,9999} 
(1 row) 

затем анализировать LATERAL JOIN:

t=# explain analyze select link_ids,max(r) from t, unnest(length) r where link_ids = '{1,9999}' group by 1; 
                 QUERY PLAN 
----------------------------------------------------------------------------------------------------------------------- 
GroupAggregate (cost=0.29..4.81 rows=1 width=33) (actual time=0.030..0.030 rows=1 loops=1) 
    -> Nested Loop (cost=0.29..4.30 rows=100 width=33) (actual time=0.025..0.027 rows=2 loops=1) 
     -> Index Scan using t_pkey on t (cost=0.29..2.30 rows=1 width=58) (actual time=0.015..0.016 rows=1 loops=1) 
       Index Cond: (link_ids = '{1,9999}'::integer[]) 
     -> Function Scan on unnest r (cost=0.00..1.00 rows=100 width=4) (actual time=0.007..0.007 rows=2 loops=1) 
Total runtime: 0.059 ms 
(6 rows) 

и попробуйте подзапрос:

t=# explain analyze select link_ids, (select max(r) from unnest(length) r) from t where link_ids = '{1,9999}'; 
                 QUERY PLAN 
----------------------------------------------------------------------------------------------------------------------- 
Index Scan using t_pkey on t (cost=0.29..3.56 rows=1 width=58) (actual time=0.030..0.031 rows=1 loops=1) 
    Index Cond: (link_ids = '{1,9999}'::integer[]) 
    SubPlan 1 
    -> Aggregate (cost=1.25..1.26 rows=1 width=4) (actual time=0.011..0.011 rows=1 loops=1) 
      -> Function Scan on unnest r (cost=0.00..1.00 rows=100 width=4) (actual time=0.008..0.008 rows=2 loops=1) 
Total runtime: 0.060 ms 
(6 rows) 

и, наконец, убедитесь, что То же самое:

t=# select link_ids, (select max(r) from unnest(length) r) 
from t 
where link_ids = '{1,9999}'; 
link_ids | max 
----------+------ 
{1,9999} | 9999 
(1 row) 

t=# select link_ids,max(r) 
from t, unnest(length) r 
where link_ids = '{1,9999}' 
group by 1; 
link_ids | max 
----------+------ 
{1,9999} | 9999 
(1 row) 
Смежные вопросы