2017-10-04 3 views
2

Je comprends comment cela fonctionne, mais je ne comprends pas DISTINCT ON (expression).SQL: Que fait "DISTINCT ON (expression)"?

Prenez le premier exemple de cette capture d'écran:

enter image description here

Comment la partie (a % 2) affecte tout? Est-il en train de dire que si a % 2 est évalué à vrai, alors le retourner, puis continuer à le faire pour tous les autres tuples mais seulement retourner si la valeur retournée est distincte?

Répondre

1

a% 2 est un opérateur modulo. Vous pouvez obtenir seulement 0 ou 1 (NULL si la colonne est nullable).

Par exemple:

i | a | a%2 
1  10  0 
2  11  1 
3  12  0 
4  13  0 

code:

CREATE TABLE r(i INT, a INT); 
INSERT INTO r(i, a) VALUES (1,10), (2,11),(3,12),(4,13); 

SELECT DISTINCT ON (a%2) a 
FROM r; 

Sortie:

10 
11 

SELECT DISTINCT ON (a%2) a 
FROM r 
ORDER BY a%2,i DESC; 

Sortie:

12 
13 

Rextester Demo

2

Bien que la réponse précédente semble correcte, je ne pense pas qu'elle soit particulièrement claire.

L'extrait de la Official documentation pour PostGreSQL est la suivante ...

DISTINCT ON (expression [...]) ne conserve que la première ligne de chaque ensemble de lignes où les expressions données à évaluer égal. [...] Notez que la "première rangée" de chaque ensemble est imprévisible, sauf si ORDER BY est utilisé pour s'assurer que la ligne désirée apparaît en premier. [...] Les expressions DISTINCT ON doivent correspondre à la (aux) expression (s) ORDER BY la plus à gauche.

Le premier point est que tout ce que vous mettez dans le ON(), doit venir d'abord en l'ORDER BY, pour des raisons qui nous l'espérons bientôt devenir clair ...

SELECT DISTINCT ON (a) a, b, c FROM a_table ORDER BY a, b 

Les résultats sont ensuite filtrés, de sorte que pour chacune des entités distinctes, seulement la première ligne est effectivement retournée.


Par exemple ...

CREATE TABLE example (
    id    INT, 
    person_id  INT, 
    address_id  INT, 
    effective_date DATE 
); 

INSERT INTO 
    example (id, person_id, address_id, effective_date) 
VALUES 
    (1, 2, 1, '2000-01-01'), -- Moved to first house 
    (5, 2, 2, '2004-08-19'), -- Went to uni 
    (9, 2, 1, '2007-06-12'), -- Moved back home 

    (2, 4, 3, '2007-05-18'), -- Moved to first house 
    (3, 4, 4, '2016-02-09') -- Moved to new house 
; 

SELECT DISTINCT ON (person_id) 
    * 
FROM 
    example 
ORDER BY 
    person_id, 
    effective_date DESC 
; 

Cette ordonnera les résultats de sorte que tous les enregistrements pour chaque personne sont contiguës, commandés à l'enregistrement le plus récent au plus ancien. Ensuite, pour chaque personne, sur le premier enregistrement est retourné. Ainsi, en donnant l'adresse la plus récente pour chaque personne.

Step 1 : Apply the ORDER BY... 

id | person_id | address_id | effective_date 
----+-----------+------------+---------------- 
    9 |  2 |  1  | '2007-06-12' 
    5 |  2 |  2  | '2004-08-19' 
    1 |  2 |  1  | '2000-01-01' 
    3 |  4 |  4  | '2016-02-09' 
    2 |  4 |  3  | '2007-05-18' 

Step 2 : filter to just the first row per person_id 

id | person_id | address_id | effective_date 
----+-----------+------------+---------------- 
    9 |  2 |  1  | '2007-06-12' 
    3 |  4 |  4  | '2016-02-09' 


Il est à peu près équivalent à ce qui suit ...

SELECT 
    * 
FROM 
(
    SELECT 
     *, 
     ROW_NUMBER() OVER (PARTITION BY person_id 
           ORDER BY effective_date DESC) AS person_address_ordinal 
    FROM 
     example 
) 
    AS sorted_example 
WHERE 
    person_address_ordinal = 1 


Quant à la question sur ce que (a % 2) fait, il est juste un calcul mathématique pour MOD(a, 2) , donc vous pouvez faire ce qui suit ...

CREATE TABLE example (
    id    INT, 
    score   INT 
); 

INSERT INTO 
    example (id, score) 
VALUES 
    (1, 2), 
    (2, 6), 
    (3, 5), 
    (4, 3), 
    (5, 4), 
; 

SELECT DISTINCT ON (id % 2) 
    * 
FROM 
    example 
ORDER BY 
    id % 2, 
    score DESC 
; 

Cela donnerait le meilleur score pour la même id de (où id % 2 égal 0), le score le plus élevé de la id s impair (où id % 2 égal 1).

Step 1 : Apply the ORDER BY... 

id | score 
----+------- 

    2 | 6  -- id % 2 = 0 
    4 | 3  -- id % 2 = 0 

    3 | 5  -- id % 2 = 1 
    5 | 4  -- id % 2 = 1 
    1 | 2  -- id % 2 = 1 

Step 2 : filter to just the first row per `id % 2` 

id | score 
----+------- 
    2 | 6  -- id % 2 = 0 
    3 | 5  -- id % 2 = 1