Supondo que tenhamos uma tabela com quatro colunas (a,b,c,d)
do mesmo tipo de dados.
É possível selecionar todos os valores distintos nos dados nas colunas e retorná-los como uma única coluna ou tenho que criar uma função para conseguir isso?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
fonte
fonte
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Respostas:
Atualização: foram testadas todas as 5 consultas no SQLfiddle com 100K linhas (e 2 casos separados, um com poucos (25) valores distintos e outro com lotes (valores em torno de 25K).
Uma consulta muito simples seria usar
UNION DISTINCT
.Eu acho que seria mais eficiente se houvesse um índice separado em cada uma das quatro colunas. Seria eficiente com um índice separado em cada uma das quatro colunas, se o Postgres tivesse implementado a otimização de Loose Index Scan , o que não existe. Portanto, essa consulta não será eficiente, pois requer 4 varreduras da tabela (e nenhum índice é usado):Outro seria primeiro
UNION ALL
e depois usarDISTINCT
. Isso também exigirá 4 varreduras de tabela (e nenhum uso de índices). Não é uma eficiência ruim quando os valores são poucos e, com mais valores, se torna o mais rápido no meu (não extenso) teste:As outras respostas forneceram mais opções usando funções de matriz ou a
LATERAL
sintaxe. A consulta de Jack (187 ms, 261 ms
) tem um desempenho razoável, mas a consulta de AndriyM parece mais eficiente (125 ms, 155 ms
). Ambos fazem uma varredura seqüencial da tabela e não usam nenhum índice.Na verdade, os resultados da consulta de Jack são um pouco melhores do que os mostrados acima (se removermos o
order by
) e podem ser melhorados removendo os 4 internosdistinct
e deixando apenas o externo.Finalmente, se - e somente se - os valores distintos das 4 colunas forem relativamente poucos, você poderá usar o
WITH RECURSIVE
hack / otimização descrito na página Loose Index Scan acima e usar todos os 4 índices, com resultados notavelmente rápidos! Testado com as mesmas 100K linhas e aproximadamente 25 valores distintos espalhados pelas 4 colunas (é executado em apenas 2 ms!), Enquanto que com 25K valores distintos, é o mais lento com 368 ms:SQLfiddle
Para resumir, quando os valores distintos são poucos, a consulta recursiva é a vencedora absoluta, enquanto com muitos valores, a minha segunda, as consultas de Jack (versão melhorada abaixo) e AndriyM são as melhores.
Adições tardias, uma variação na 1ª consulta que, apesar das operações extra distintas, tem um desempenho muito melhor que a 1ª original e apenas um pouco pior que a 2ª:
e Jack melhorou:
fonte
Você pode usar LATERAL, como nesta consulta :
A palavra-chave LATERAL permite que o lado direito da junção faça referência a objetos do lado esquerdo. Nesse caso, o lado direito é um construtor VALUES que cria um subconjunto de coluna única a partir dos valores da coluna que você deseja colocar em uma única coluna. A consulta principal simplesmente faz referência à nova coluna, também aplicando DISTINCT a ela.
fonte
Para ser claro, eu usaria
union
como o ypercube sugere , mas também é possível com matrizes:dbfiddle aqui
fonte
Mais curto
Uma versão menos detalhada da idéia de Andriy é apenas um pouco mais longa, mas mais elegante e mais rápida.
Para muitos valores distintos / poucos duplicados:
O mais rápido
Com um índice em cada coluna envolvida!
Para alguns valores distintos / muitos duplicados:
Esta é outra variante do rCTE, semelhante à que o @ypercube já postou , mas eu uso em
ORDER BY 1 LIMIT 1
vez damin(a)
qual normalmente é um pouco mais rápida. Também não preciso de nenhum predicado adicional para excluir valores NULL.E em
LATERAL
vez de uma subconsulta correlacionada, porque é mais limpa (não necessariamente mais rápida).Explicação detalhada em minha resposta a seguir para esta técnica:
Atualizei o SQL Fiddle do ypercube e adicionei o meu à lista de reprodução.
fonte
EXPLAIN (ANALYZE, TIMING OFF)
para verificar o melhor desempenho geral? (Melhor de 5 para excluir efeitos cache.)VALUES ...
é mais rápida queunnest(ARRAY[...])
.LATERAL
está implícito para funções de retorno de conjunto naFROM
lista.Você pode, mas, ao escrever e testar a função, senti-me errado. É um desperdício de recursos.
Apenas use uma união e mais opções. Única vantagem (se for): uma única verificação da tabela principal.
No sql fiddle, você precisa alterar o separador de $ para outra coisa, como /
fonte