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

Qual é a diferença entre Postgres DISTINCT vs DISTINCT ON?


DISTINCT e DISTINCT ON têm semânticas completamente diferentes.

Primeiro a teoria

DISTINCT se aplica a uma tupla inteira. Depois que o resultado da consulta é calculado, DISTINCT remove quaisquer tuplas duplicadas do resultado.

Por exemplo, suponha uma tabela R com o seguinte conteúdo:
#table r;
a | b 
---+---
1 | a
2 | b
3 | c
3 | d
2 | e
1 | a

(6 linhas)

SELECT distinto * de R resultará:
# select distinct * from r;
 a | b 
---+---
 1 | a
 3 | d
 2 | e
 2 | b
 3 | c
(5 rows)

Observe que distinct se aplica a toda a lista de atributos projetados:assim
select distinct * from R

é semanticamente equivalente a
select distinct a,b from R

Você não pode emitir
select a, distinct b From R

DISTINCT deve seguir SELECT. Aplica-se à tupla inteira, não a um atributo do resultado.

DISTINTO LIGADO é uma adição postgresql à linguagem. É semelhante, mas não idêntico, agrupar por.

Sua sintaxe é:
 SELECT DISTINCT ON (attributeList) <rest as any query>

Por exemplo:
 SELECT DISTINCT ON (a) * from R

A semântica pode ser descrita da seguinte forma. Calcule a consulta como de costume -- sem o DISTINCT ON (a) --- mas antes da projeção do resultado, ordene o resultado atual e agrupe-o de acordo com a lista de atributos em DISTINCT ON (semelhante a agrupar por). Agora, faça a projeção usando a primeira tupla de cada grupo e ignore as outras tuplas.

Exemplo:
select distinct * from r order by a;
     a | b 
    ---+---
     1 | a
     2 | e
     2 | b
     3 | c
     3 | d
    (5 rows)

Então, para cada valor diferente de a, pegue a primeira tupla. Que é o mesmo que:
 SELECT DISTINCT on (a) * from r;
  a | b 
 ---+---
 1 | a
 2 | b
 3 | c
 (3 rows)

Alguns DBMS (principalmente sqlite) permitirão que você execute esta consulta:
 SELECT a,b from R group by a;

E isso lhe dá um resultado semelhante.

O Postgresql permitirá esta consulta, se e somente se houver uma dependência funcional de a para b. Em outras palavras, esta consulta será válida se para qualquer instância da relação R, houver apenas uma tupla única para cada valor ou a (portanto, selecionar a primeira tupla é determinístico:há apenas uma tupla).

Por exemplo, se a chave primária de R for a, então a->b e:
SELECT a,b FROM R group by a

é idêntico a:
  SELECT DISTINCT on (a) a, b from r;

Agora, de volta ao seu problema:

Primeira consulta:
SELECT DISTINCT count(dimension1)
FROM data_table;

calcula a contagem de dimension1 (número de tuplas em data_table onde dimension1 não é nulo). Esta consulta retorna uma tupla, que é sempre única (portanto, DISTINCT é redundante).

Pergunta 2:
SELECT count(*)
FROM (SELECT DISTINCT ON (dimension1) dimension1
FROM data_table
GROUP BY dimension1) AS tmp_table;

Esta é uma consulta em uma consulta. Deixe-me reescrevê-lo para maior clareza:
WITH tmp_table AS (
   SELECT DISTINCT ON (dimension1) 
     dimension1 FROM data_table
     GROUP by dimension1) 
SELECT count(*) from tmp_table

Vamos calcular primeiro tmp_table. Como mencionei acima, vamos primeiro ignorar o DISTINCT ON e fazer o resto da consulta. Este é um grupo por dimensão1. Portanto, essa parte da consulta resultará em uma tupla por valor diferente de dimension1.

Agora, o DISTINTO ON. Ele usa dimension1 novamente. Mas dimension1 já é único (devido ao group by). Portanto, isso torna o DISTINCT ON superflouos (não faz nada). A contagem final é simplesmente uma contagem de todas as tuplas do grupo por.

Como você pode ver, há uma equivalência na seguinte consulta (aplica-se a qualquer relação com um atributo a):
SELECT (DISTINCT ON a) a
FROM R

e
SELECT a FROM R group by a

e
SELECT DISTINCT a FROM R

Aviso

O uso de resultados DISTINCT ON em uma consulta pode ser não determinístico para qualquer instância do banco de dados. Em outras palavras, a consulta pode retornar resultados diferentes para as mesmas tabelas.

Um aspecto interessante

Distinct ON emula um ruim comportamento do sqlite de uma forma muito mais limpa. Suponha que R tenha dois atributos a e b:
SELECT a, b FROM R group by a

é uma instrução ilegal em SQL. No entanto, ele é executado em sqlite. Ele simplesmente pega um valor aleatório de b de qualquer uma das tuplas no grupo de mesmos valores de a. No Postgresql esta declaração é ilegal. Em vez disso, você deve usar DISTINCT ON e escrever:
SELECT DISTINCT ON (a) a,b from R

Corolário

DISTINCT ON é útil em um grupo por quando você deseja acessar um valor que é funcionalmente dependente do grupo por atributos. Em outras palavras, se você sabe que para cada grupo de atributos eles sempre têm o mesmo valor do terceiro atributo, então use DISTINCT ON nesse grupo de atributos. Caso contrário, você teria que fazer um JOIN para recuperar esse terceiro atributo.