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):
-- Query 1. (334 ms, 368ms)
SELECT a AS abcd FROM tablename
UNION -- means UNION DISTINCT
SELECT b FROM tablename
UNION
SELECT c FROM tablename
UNION
SELECT d FROM tablename ;
Outro seria primeiro UNION ALL
e depois usar DISTINCT
. 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:
-- Query 2. (87 ms, 117 ms)
SELECT DISTINCT a AS abcd
FROM
( SELECT a FROM tablename
UNION ALL
SELECT b FROM tablename
UNION ALL
SELECT c FROM tablename
UNION ALL
SELECT d FROM tablename
) AS x ;
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 internos distinct
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:
-- Query 3. (2 ms, 368ms)
WITH RECURSIVE
da AS (
SELECT min(a) AS n FROM observations
UNION ALL
SELECT (SELECT min(a) FROM observations
WHERE a > s.n)
FROM da AS s WHERE s.n IS NOT NULL ),
db AS (
SELECT min(b) AS n FROM observations
UNION ALL
SELECT (SELECT min(b) FROM observations
WHERE b > s.n)
FROM db AS s WHERE s.n IS NOT NULL ),
dc AS (
SELECT min(c) AS n FROM observations
UNION ALL
SELECT (SELECT min(c) FROM observations
WHERE c > s.n)
FROM dc AS s WHERE s.n IS NOT NULL ),
dd AS (
SELECT min(d) AS n FROM observations
UNION ALL
SELECT (SELECT min(d) FROM observations
WHERE d > s.n)
FROM db AS s WHERE s.n IS NOT NULL )
SELECT n
FROM
( TABLE da UNION
TABLE db UNION
TABLE dc UNION
TABLE dd
) AS x
WHERE n IS NOT NULL ;
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ª:
-- Query 1b. (85 ms, 149 ms)
SELECT DISTINCT a AS n FROM observations
UNION
SELECT DISTINCT b FROM observations
UNION
SELECT DISTINCT c FROM observations
UNION
SELECT DISTINCT d FROM observations ;
e Jack melhorou:
-- Query 4b. (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
array_agg(b)||
array_agg(c)||
array_agg(d) )
from t ;
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?