PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Qual è la differenza tra Postgres DISTINCT e DISTINCT ON?

DISTINCT e DISTINCT ON hanno una semantica completamente diversa.

Prima la teoria

DISTINCT si applica a un'intera tupla. Una volta calcolato il risultato della query, DISTINCT rimuove tutte le tuple duplicate dal risultato.

Ad esempio, supponiamo una tabella R con i seguenti contenuti:

#table r;
a | b 
---+---
1 | a
2 | b
3 | c
3 | d
2 | e
1 | a

(6 righe)

SELECT distinto * da R risulterà:

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

Nota che distinto si applica all'intero elenco di attributi proiettati:quindi

select distinct * from R

è semanticamente equivalente a

select distinct a,b from R

Non puoi emettere

select a, distinct b From R

DISTINCT deve seguire SELECT. Si applica all'intera tupla, non a un attributo del risultato.

DISTINTA ATTIVA è un'aggiunta postgresql al linguaggio. È simile, ma non identico, raggruppare per.

La sua sintassi è:

 SELECT DISTINCT ON (attributeList) <rest as any query>

Ad esempio:

 SELECT DISTINCT ON (a) * from R

La semantica può essere descritta come segue. Calcola la query come al solito, senza DISTINCT ON (a)---ma prima della proiezione del risultato, ordina il risultato corrente e raggruppalo in base all'elenco di attributi in DISTINCT ON (simile al raggruppamento per). Ora, esegui la proiezione usando la prima tupla in ogni gruppo e ignora le altre tuple.

Esempio:

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

Quindi, per ogni diverso valore di a, prendi la prima tupla. Che è uguale a:

 SELECT DISTINCT on (a) * from r;
  a | b 
 ---+---
 1 | a
 2 | b
 3 | c
 (3 rows)

Alcuni DBMS (in particolare sqlite) ti permetteranno di eseguire questa query:

 SELECT a,b from R group by a;

E questo ti dà un risultato simile.

Postgresql consentirà questa query, se e solo se esiste una dipendenza funzionale da a a b. In altre parole, questa query sarà valida se per qualsiasi istanza della relazione R esiste una sola tupla univoca per ogni valore o a (quindi selezionare la prima tupla è deterministico:esiste una sola tupla).

Ad esempio, se la chiave primaria di R è a, allora a->b e:

SELECT a,b FROM R group by a

è identico a:

  SELECT DISTINCT on (a) a, b from r;

Ora, torniamo al tuo problema:

Prima domanda:

SELECT DISTINCT count(dimension1)
FROM data_table;

calcola il conteggio di dimension1 (numero di tuple in data_table che dove dimension1 non è null). Questa query restituisce una tupla, che è sempre unica (quindi DISTINCT è ridondante).

Domanda 2:

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

Questa è una query in una query. Lascia che lo riscriva per chiarezza:

WITH tmp_table AS (
   SELECT DISTINCT ON (dimension1) 
     dimension1 FROM data_table
     GROUP by dimension1) 
SELECT count(*) from tmp_table

Calcoliamo prima tmp_table. Come accennato in precedenza, ignoriamo prima DISTINCT ON e facciamo il resto della query. Questo è un gruppo per dimensione1. Quindi questa parte della query risulterà in una tupla per diverso valore di dimensione1.

Ora, il DISTINCT ON. Utilizza di nuovo dimensione1. Ma la dimensione1 è già unica (a causa del raggruppamento). Quindi questo rende il DISTINCT ON superfluo (non fa nulla). Il conteggio finale è semplicemente un conteggio di tutte le tuple nel gruppo di.

Come puoi vedere, c'è un'equivalenza nella seguente query (si applica a qualsiasi relazione con un attributo a):

SELECT (DISTINCT ON a) a
FROM R

e

SELECT a FROM R group by a

e

SELECT DISTINCT a FROM R

Avviso

L'utilizzo di DISTINCT ON risultati in una query potrebbe non essere deterministico per una determinata istanza del database. In altre parole, la query potrebbe restituire risultati diversi per le stesse tabelle.

Un aspetto interessante

Distinct ON emula un cattivo comportamento di sqlite in un modo molto più pulito. Supponiamo che R abbia due attributi aeb:

SELECT a, b FROM R group by a

è un'istruzione illegale in SQL. Eppure, funziona su sqlite. Prende semplicemente un valore casuale di b da una qualsiasi delle tuple nel gruppo degli stessi valori di a. In Postgresql questa affermazione è illegale. Invece, devi usare DISTINCT ON e scrivere:

SELECT DISTINCT ON (a) a,b from R

Corollario

DISTINCT ON è utile in un gruppo per quando si desidera accedere a un valore che dipende funzionalmente dal raggruppamento per attributi. In altre parole, se sai che per ogni gruppo di attributi hanno sempre lo stesso valore del terzo attributo, usa DISTINCT ON quel gruppo di attributi. Altrimenti dovresti fare un JOIN per recuperare quel terzo attributo.