PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Use algo como TOP com GROUP BY


Você pode recuperar convenientemente o passageiro com o nome mais longo por grupo com DISTINCT ON .

Mas não vejo como combinar isso (ou qualquer outra maneira simples) com sua consulta original em um único SELECT . Sugiro juntar duas subconsultas separadas:
SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);

USING na cláusula join convenientemente gera apenas uma instância de orig , então você pode simplesmente usar SELECT * no SELECT externo .

Se passenger pode ser NULL, é importante adicionar NULLS LAST :

De vários nomes de passageiros com o mesmo comprimento máximo no mesmo grupo, você recebe uma escolha arbitrária - a menos que você adicione mais expressões a ORDER BY como desempate. Explicação detalhada na resposta vinculada acima.

Desempenho?


Normalmente, uma única varredura é superior, especialmente com varreduras sequenciais.

A consulta acima usa dois varreduras (talvez varreduras de índice / somente índice). Mas a segunda varredura é comparativamente barata, a menos que a tabela seja muito grande para caber no cache (principalmente). Lukas sugeriu uma consulta alternativa com apenas um único SELECT adicionando:
, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST

A ideia é inteligente, mas última vez que testei , array_agg com ORDER BY não teve um desempenho tão bom. (A sobrecarga de ORDER BY por grupo é substancial, e o manuseio de array também é caro.)

A mesma abordagem pode ser mais barata com uma função de agregação personalizada first() como instruído no Postgres Wiki aqui . Ou, ainda mais rápido, com uma versão escrita em C, disponível no PGXN . Elimina o custo extra para manipulação de array, mas ainda precisamos de ORDER BY por grupo . Pode ser mais rápido para poucos grupos. Você então adicionaria:
 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)

Gordon e Lukas mencione também a função de janela first_value() . As funções da janela são aplicadas depois funções agregadas. Para usá-lo no mesmo SELECT , precisaríamos agregar passenger de alguma forma primeiro - pegue 22. Gordon resolve isso com uma subconsulta - outro candidato para um bom desempenho com o Postgres padrão.

first() faz o mesmo sem subconsulta e deve ser mais simples e um pouco mais rápido. Mas ainda não será mais rápido do que um DISTINCT ON separado para a maioria dos casos com poucas linhas por grupo. Para muitas linhas por grupo, uma técnica CTE recursiva normalmente é mais rápida. Existem técnicas ainda mais rápidas se você tiver uma tabela separada contendo todos os orig relevantes e exclusivos valores. Detalhes:

A melhor solução depende de vários fatores. A prova do pudim está no comer. Para otimizar o desempenho, você precisa testar com sua configuração. A consulta acima deve estar entre as mais rápidas.