J'ai un tableau avec les commentaires des utilisateurs dans un livre d'or. Les colonnes sont: id, id_utilisateur, titre, commentaire, horodatage.
Je dois sélectionner la dernière ligne pour chaque utilisateur. J'ai essayé de le faire avec group by mais je n'y suis pas parvenu car je ne peux rien sélectionner d'autre dans la même requête où je groupe par user_id:
SELECT user_id, MAX(ts) FROM comments GROUP BY user_id
par exemple, dans cette requête, je ne peux pas ajouter pour sélectionner également les colonnes id, tilte et comment. Comment cela peut-il être fait?
Vous pouvez utiliser des fonctions analytiques
SELECT *
FROM (SELECT c.*,
rank() over (partition by user_id order by ts desc) rnk
FROM comments c)
WHERE rnk = 1
Selon la façon dont vous souhaitez gérer les liens (s'il peut y avoir deux lignes avec le même user_id
et ts
), vous pouvez utiliser le row_number
ou dense_rank
fonction plutôt que rank
. rank
permettrait à plusieurs lignes d'être les premières s'il y avait une égalité. row_number
renverrait arbitrairement une ligne en cas d'égalité. dense_rank
se comporterait comme rank
pour les lignes liées pour la première mais considérerait la ligne suivante comme deuxième plutôt que troisième en supposant que deux lignes sont liées pour la première.
Vous pouvez développer votre requête en utilisant un JOIN
:
select c.*
from comments c join
(select user_id, max(ts) as maxts
from comments c2
group by user_id
) cc
on c.user_id = cc.user_id and c.ts = cc.maxts;
Il existe d'autres moyens. Un conseil typique est d'utiliser row_number()
:
select t.*
from (select c.*, row_number() over (partition by user_id order by ts desc) as seqnum
from comments c
) c
where seqnum = 1;
Ces deux requêtes sont subtilement différentes. Le premier retournera des doublons si le commentaire le plus récent pour un utilisateur avait exactement le même ts
. La seconde renvoie une ligne par utilisateur.
Ce type de problèmes a une solution très simple et très efficace avec le dense rank first/last
fonction:
select id,
max(user_id) keep (dense_rank last order by ts) over (partition by id) as user_id,
max(title) keep (dense_rank last order by ts) over (partition by id) as title,
max(comment) keep (dense_rank last order by ts) over (partition by id) as comment,
max(ts) as ts
from comments;