PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Quelle est la différence entre Postgres DISTINCT et DISTINCT ON ?

DISTINCT et DISTINCT ON ont une sémantique complètement différente.

D'abord la théorie

DISTINCT s'applique à un tuple entier. Une fois le résultat de la requête calculé, DISTINCT supprime tous les tuples en double du résultat.

Par exemple, supposons une table R avec le contenu suivant :

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

(6 lignes)

SELECT distinct * de R donnera :

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

Notez que distinct s'applique à toute la liste des attributs projetés :ainsi

select distinct * from R

est sémantiquement équivalent à

select distinct a,b from R

Vous ne pouvez pas émettre

select a, distinct b From R

DISTINCT doit suivre SELECT. Il s'applique à l'ensemble du tuple, pas à un attribut du résultat.

DISTINCT ON est un ajout postgresql au langage. Il est similaire, mais pas identique, au groupe par.

Sa syntaxe est :

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

Par exemple :

 SELECT DISTINCT ON (a) * from R

Sa sémantique peut être décrite comme suit. Calculez la requête comme d'habitude -- sans le DISTINCT ON (a) --- mais avant la projection du résultat, triez le résultat actuel et regroupez-le selon la liste d'attributs dans DISTINCT ON (similaire à group by). Maintenant, faites la projection en utilisant le premier tuple de chaque groupe et ignorez les autres tuples.

Exemple :

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

Ensuite, pour chaque valeur différente de a, prenez le premier tuple. Qui est identique à :

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

Certains SGBD (notamment sqlite) vous permettront d'exécuter cette requête :

 SELECT a,b from R group by a;

Et cela vous donne un résultat similaire.

Postgresql autorisera cette requête, si et seulement s'il existe une dépendance fonctionnelle de a à b. En d'autres termes, cette requête sera valide si pour toute instance de la relation R, il n'y a qu'un seul tuple pour chaque valeur ou a (la sélection du premier tuple est donc déterministe :il n'y a qu'un seul tuple).

Par exemple, si la clé primaire de R est a, alors a->b et :

SELECT a,b FROM R group by a

est identique à :

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

Maintenant, revenons à votre problème :

Première requête :

SELECT DISTINCT count(dimension1)
FROM data_table;

calcule le nombre de dimension1 (nombre de tuples dans data_table où dimension1 n'est pas nul). Cette requête renvoie un tuple, qui est toujours unique (donc DISTINCTest redondant).

Requête 2 :

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

Il s'agit d'une requête dans une requête. Permettez-moi de le réécrire pour plus de clarté :

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

Calculons d'abord tmp_table. Comme je l'ai mentionné ci-dessus, ignorons d'abord le DISTINCT ON et faisons le reste de la requête. Il s'agit d'un regroupement par dimension1. Par conséquent, cette partie de la requête aboutira à un tuple par valeur différente de dimension1.

Maintenant, le DISTINCT ON. Il utilise à nouveau dimension1. Mais dimension1 est déjà unique (en raison du groupe par). Par conséquent, cela rend le DISTINCT ON superfluos (il ne fait rien). Le décompte final est simplement un décompte de tous les tuples du groupe by.

Comme vous pouvez le voir, il y a une équivalence dans la requête suivante (elle s'applique à toute relation avec un attribut a) :

SELECT (DISTINCT ON a) a
FROM R

et

SELECT a FROM R group by a

et

SELECT DISTINCT a FROM R

Avertissement

L'utilisation de résultats DISTINCT ON dans une requête peut être non déterministe pour une instance donnée de la base de données. En d'autres termes, la requête peut renvoyer des résultats différents pour les mêmes tables.

Un aspect intéressant

Distinct ON émule un mauvais comportement de sqlite d'une manière beaucoup plus propre. Supposons que R possède deux attributs a et b :

SELECT a, b FROM R group by a

est une instruction illégale en SQL. Pourtant, il fonctionne sur sqlite. Il prend simplement une valeur aléatoire de b à partir de n'importe lequel des tuples du groupe de mêmes valeurs de a. Dans Postgresql, cette déclaration est illégale. A la place, vous devez utiliser DISTINCT ON et écrire :

SELECT DISTINCT ON (a) a,b from R

Corollaire

DISTINCT ON est utile dans un groupe par lorsque vous souhaitez accéder à une valeur fonctionnellement dépendante des attributs du groupe par. En d'autres termes, si vous savez que pour chaque groupe d'attributs, ils ont toujours la même valeur du troisième attribut, utilisez alors DISTINCT SUR ce groupe d'attributs. Sinon, vous devrez faire un JOIN pour récupérer ce troisième attribut.