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