Supondo que tenhamos uma tabela com quatro colunas (a,b,c,d)
do mesmo tipo de dados.
É possível selecionar todos os valores distintos dentro dos dados nas colunas e devolvê-los como uma única coluna ou tenho que criar uma função para conseguir isso?
Atualização: testei todas as 5 consultas no SQLfiddle com 100K linhas (e 2 casos separados, um com poucos (25) valores distintos e outro com muitos (cerca de 25K valores).
Uma consulta muito simples seria usar
UNION DISTINCT
.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 Loose Index Scan , o que não aconteceu. Portanto, esta consulta não será eficiente, pois requer 4 varreduras da tabela (e nenhum índice é usado):Outra seria primeiro
UNION ALL
e depois usarDISTINCT
. Isso também exigirá 4 varreduras de tabela (e nenhum uso de índices). Não é má eficiência quando os valores são poucos, e com mais valores se torna o mais rápido no meu teste (não extenso):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 desempenho razoável, mas a consulta de AndriyM parece mais eficiente (125 ms, 155 ms
). Ambos fazem uma varredura sequencial 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 ainda mais removendo os 4 internosdistinct
e deixando apenas o externo.Finalmente, se - e somente se - os valores distintos das 4 colunas forem relativamente poucos, você pode 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 100 mil linhas e aproximadamente 25 valores distintos espalhados pelas 4 colunas (executou em apenas 2 ms!), enquanto com 25 mil 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, minha segunda, as consultas de Jack (versão melhorada abaixo) e AndriyM são as de melhor desempenho.
Adições tardias, uma variação da 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:
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 de coluna que você deseja colocar em uma única coluna. A consulta principal simplesmente faz referência à nova coluna, aplicando também DISTINCT a ela.
Para ser claro, eu usaria
union
como ypercube sugere , mas também é possível com arrays:dbfiddle aqui
Mais curto
Uma versão menos detalhada da ideia de Andriy é apenas um pouco mais longa, mas mais elegante e 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
ORDER BY 1 LIMIT 1
em vez domin(a)
que normalmente é um pouco mais rápido. Também não preciso de predicado adicional para excluir valores NULL.E
LATERAL
em vez de uma subconsulta correlacionada, porque é mais limpa (não necessariamente mais rápida).Explicação detalhada na minha resposta para esta técnica:
Eu o adicionei ao sqlfiddle do ypercube
... e agora portei isso para o dbfiddle.uk, pois o sqlfiddle.com não está acompanhando:
db<>fique aqui
Você pode, mas como eu escrevi e testei a função me senti errado. É um desperdício de recursos.
Apenas por favor use uma união e mais selecione. Única vantagem (se for), uma única varredura da tabela principal.
No violino sql você precisa alterar o separador de $ para outra coisa, como /