626 votes

Extraction de la ligne qui a la valeur maximale d'une colonne

Tableau: Nom D'Utilisateur, La Valeur, La Date.

Je veux obtenir le nom d'utilisateur, la Valeur de max(Date) pour chaque code d'utilisateur. C'est la Valeur pour chaque nom d'utilisateur qui a la date la plus récente. Est-il un moyen de le faire simplement en SQL? (De Préférence Oracle)

Mise à jour: toutes mes Excuses pour toute ambiguïté: j'ai besoin d'obtenir TOUTES les Identifiants. Mais pour chaque nom d'utilisateur, seule la ligne où l'utilisateur a la date la plus récente.

479voto

Bill Karwin Points 204877

Je vois beaucoup de gens l'utilisation de sous-requêtes ou autre fournisseur de fonctionnalités spécifiques pour ce faire, mais je fais souvent ce genre de requête sans les sous-requêtes de la manière suivante. Il utilise la plaine, le standard SQL, donc il devrait fonctionner dans tous les SGBD.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

En d'autres termes: extraction de la ligne de t1 où aucune autre ligne existe avec le même nom d'utilisateur et un plus grand Jour.

(J'ai mis de l'identificateur de type "Date" dans les délimiteurs parce que c'est un mot réservé SQL.)

Dans le cas où si t1."Date" = t2."Date", en doublant apparaît. Généralement, les tables a auto_inc(seq), par exemple, Id. Pour éviter de doubler peut être utilisée suit:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

Re commentaire de @Farhan:

Voici une explication plus détaillée:

Une jointure externe tente de rejoindre t1 avec t2. Par défaut, tous les résultats de t1 sont retournés, et si il y a un match en t2, il est également renvoyé. Si il n'y a pas de correspondance dans la t2 pour une ligne donnée de t1, la requête renvoie toujours la ligne de t1, et NULL est utilisé comme un espace réservé pour tous les t2 colonnes. C'est juste la façon dont les jointures externes travail en général.

L'astuce de cette requête est la conception de la jointure de la condition de correspondance tel que t2 doit correspondre à la même nom d'utilisateur, et une grande date. L'idée étant, si une ligne existe en t2 qui a plus de date, puis la ligne t1 c'est comparée ne peut pas être la plus grande date pour ce nom d'utilisateur. Mais si il n'y a pas de correspondance -- c'est à dire si aucune ligne n'existe en t2 avec un plus grand jour que la ligne t1-nous savons que la ligne t1 a été la ligne avec la plus grande date pour le nom d'utilisateur.

Dans ces cas (quand il n'y a pas de match), les colonnes de t2 sera NULLE, même les colonnes spécifiées dans la condition de jointure. C'est pourquoi nous utilisons WHERE t2.UserId IS NULL, parce que nous sommes à la recherche pour les cas où aucune ligne n'a été trouvé avec une grande date pour le nom d'utilisateur.

432voto

David Aldridge Points 27624

Cela permettra de récupérer toutes les lignes pour lesquelles la my_date valeur de la colonne est égale à la valeur maximale de my_date pour que userid. Cela peut extraire plusieurs lignes pour le nom où le maximum de la date est sur plusieurs lignes.

select userid,
       my_date,
       ...
from
(
select userid,
       my_Date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"Fonctions analytiques rock"

Edit: en ce qui concerne le premier commentaire ...

"à l'aide de requêtes analytiques et une auto-jointure défait le but de l'analyse des requêtes"

Il n'y a pas d'auto-jointure dans ce code. Il s'agit plutôt d'un prédicat placés sur le résultat de la ligne qui contient la fonction analytique -- une toute autre affaire, et complètement pratique standard.

"La fenêtre par défaut dans Oracle est à partir de la première ligne de la partition de l'actuel"

Le fenêtrage clause n'est applicable qu'en présence de la clause order by. Avec pas de clause order by, pas de fenêtre clause est appliquée par défaut et aucune ne peut être spécifié explicitement.

Le code fonctionne.

167voto

Dave Costa Points 25282
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

54voto

Steve K Points 10475

Je ne sais pas exactement à vos noms de colonnes, mais il serait quelque chose comme ceci:

 sélectionnez nom d'utilisateur, la valeur
 des utilisateurs u1
 où date = (select max(date)
 des utilisateurs u2
 où u1.userid = u2.userid)

42voto

Mike Woodhouse Points 27748

N'étant pas au travail, je n'ai pas d'Oracle à la main, mais il me semble que l'Oracle permet à plusieurs colonnes pour être assorti d'une clause, qui devrait au moins éviter les options utiliser une sous-requête en corrélation, ce qui est rarement une bonne idée.

Quelque chose comme ça, peut-être (ne me souviens pas si la liste de colonnes doit être parenthesised ou pas):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: Juste essayé pour de vrai:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

De sorte qu'il fonctionne, bien que certains de la nouvelle-fangly trucs mentionnés ailleurs peut-être plus performant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X