1
votes

Comment sélectionner uniquement les premières lignes pour chaque valeur unique d'une colonne avec SQL

Disons que j'ai ce tableau

SELECT DISTINCT name, val 
FROM answer AS sf 
LEFT JOIN user AS u ON u.id_user = sf.id_user 
WHERE sf.id_feedback = 1  
ORDER BY name

Je veux obtenir la dernière réponse pour chaque utilisateur (MAX (année) et MAX (mois))

name | val 
-----+-----
user1| YES
user2| YES
user3| NO


1 commentaires

Les champs année et mois sont-ils numériques? Ces données sont-elles vraiment stockées dans une seule table? Comme votre exemple de requête l'indique autrement.


3 Réponses :


2
votes

Premier groupe pour obtenir les dates maximales de chaque utilisateur, puis rejoignez:

select a.name, a.val 
from answer as a inner join (
  select name, max(year & month) as maxdate
  from answer
  group by name
) as g on g.name = a.name and g.maxdate = (a.year & a.month)

Si les colonnes year et month sont du texte puis:

select a.name, a.val 
from answer as a inner join (
  select name, max(dateserial(year, month, 1)) as maxdate
  from answer
  group by name
) as g on g.name = a.name and g.maxdate = dateserial(a.year, a.month, 1)


0 commentaires

0
votes

Vous pouvez utiliser une sous-requête corrélée:

select t.name, t.val
from table t 
where t.pk = (select top 1 t1.pk
              from table t1
              where t1.name = t.name
              order by t1.year desc, t1.month desc
             );

pk indique la colonne d'identité qui identifie l'ordre des colonnes.


1 commentaires

vous supposez qu'une colonne pk existe ici. Il n'y a rien dans la question sur pk.



0
votes

Voici une autre méthode, utilisant une sous-requête corrélée:

select a.name, a.val from answer a
where not exists 
(select 1 from answer b where a.name = b.name and dateserial(a.year,a.month,1) < dateserial(b.year,b.month,1))

MODIFIER: Code corrigé ci-dessous pour tenir compte des cas où les champs de l'année correspondent, mais les champs du mois diffèrent : (merci @ Scorpioo590)

select a.name, a.val from answer a
where not exists 
(select 1 from answer b where a.name = b.name and (a.year < b.year or (a.year = b.year and a.month < b.month)))

Alternativement, en utilisant dateserial:

select a.name, a.val from answer a
where not exists 
(select 1 from answer b where a.name = b.name and a.year < b.year and a.month < b.month)


3 commentaires

Cela ne fonctionnera que si l'année et le mois de l'entrée souhaitée sont plus grands que toutes les autres valeurs. Supposons que vous ayez deux lignes 2019-01 et 2019-02. Puisqu'il n'existe pas d'entrée où l'année est la plus grande, les deux entrées seraient sélectionnées


WHERE a.name = b.name AND (a.year devrait faire le travail.


@ Scorpioo590 Merci beaucoup, j'avais oublié ce cas - j'ai mis à jour ma réponse ci-dessus. Merci d'avoir attiré mon attention là-dessus.