Table:
UserId, Value, Date.
Je veux obtenir le UserId, la valeur pour le max (Date) pour chaque UserId. Autrement dit, la valeur de chaque UserId qui a la dernière date. Existe-t-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 TOUS les UserIds. Mais pour chaque UserId, seule la ligne où cet utilisateur a la dernière date.
Réponses:
Cela récupérera toutes les lignes pour lesquelles la valeur de la colonne my_date est égale à la valeur maximale de my_date pour cet ID utilisateur. Cela peut récupérer plusieurs lignes pour l'ID utilisateur où la date maximale est sur plusieurs lignes.
"Fonctions analytiques rock"
Edit: En ce qui concerne le premier commentaire ...
"L'utilisation de requêtes analytiques et d'une auto-jointure va à l'encontre de l'objectif des requêtes analytiques"
Il n'y a pas d'auto-jointure dans ce code. Il y a plutôt un prédicat placé sur le résultat de la vue en ligne qui contient la fonction analytique - une question très différente, et une pratique complètement standard.
"La fenêtre par défaut dans Oracle va de la première ligne de la partition à celle en cours"
La clause de fenêtrage n'est applicable qu'en présence de la clause order by. Sans clause order by, aucune clause de fenêtrage n'est appliquée par défaut et aucune ne peut être explicitement spécifiée.
Le code fonctionne.
la source
MAX(...) OVER (...)
vous pouvez également utiliserROW_NUMBER() OVER (...)
(pour le top-n-par-groupe) ouRANK() OVER (...)
(pour le plus grand n-par-groupe).Je vois que beaucoup de gens utilisent des sous-requêtes ou bien des fonctionnalités spécifiques au fournisseur pour ce faire, mais je fais souvent ce type de requête sans sous-requêtes de la manière suivante. Il utilise du SQL standard simple et devrait donc fonctionner dans n'importe quelle marque de SGBDR.
En d'autres termes: récupérer la ligne d'
t1
où aucune autre ligne n'existe avec la même dateUserId
et une date supérieure.(J'ai mis l'identifiant "Date" dans les délimiteurs car c'est un mot réservé SQL.)
Dans le cas où
t1."Date" = t2."Date"
, un doublement apparaît. Les tables ont généralement uneauto_inc(seq)
clé, par exempleid
. Pour éviter le doublement peut être utilisé comme suit:Re commentaire de @Farhan:
Voici une explication plus détaillée:
Une jointure externe tente de se joindre
t1
àt2
. Par défaut, tous les résultats det1
sont renvoyés et s'il y a une correspondancet2
, elle est également renvoyée. S'il n'y a pas de correspondancet2
pour une ligne donnée det1
, la requête renvoie toujours la ligne det1
et utiliseNULL
comme espace réservé pour toust2
les colonnes de. C'est ainsi que les jointures externes fonctionnent en général.L'astuce dans cette requête consiste à concevoir la condition de correspondance de la jointure telle qu'elle
t2
doit correspondre à la mêmeuserid
et à une plus grandedate
. L'idée étant que s'il existe une lignet2
qui a un plus granddate
, alors la ligne dans laquellet1
elle est comparée ne peut pas être la meilleuredate
pour celauserid
. Mais s'il n'y a pas de correspondance - c'est-à-dire s'il n'y a pas de lignet2
avec une plus grandedate
que la ligne ent1
- nous savons que la ligne ent1
était la ligne avec le plus granddate
pour le donnéuserid
.Dans ces cas (lorsqu'il n'y a pas de correspondance), les colonnes de
t2
serontNULL
- même les colonnes spécifiées dans la condition de jointure. C'est pourquoi nous utilisonsWHERE t2.UserId IS NULL
, car nous recherchons les cas où aucune ligne n'a été trouvée avec un plus granddate
pour le donnéuserid
.la source
la source
Je ne connais pas les noms exacts de vos colonnes, mais ce serait quelque chose comme ceci:
la source
N'étant pas au travail, je n'ai pas Oracle à portée de main, mais je semble me rappeler qu'Oracle permet de faire correspondre plusieurs colonnes dans une clause IN, ce qui devrait au moins éviter les options qui utilisent une sous-requête corrélée, ce qui est rarement un bon idée.
Quelque chose comme ça, peut-être (je ne me souviens pas si la liste des colonnes doit être entre parenthèses ou non):
EDIT: Je viens de l'essayer pour de vrai:
Cela fonctionne donc, même si certaines des nouveautés mentionnées ailleurs peuvent être plus performantes.
la source
Je sais que vous avez demandé Oracle, mais dans SQL 2005, nous utilisons maintenant ceci:
la source
Je n'ai pas Oracle pour le tester, mais la solution la plus efficace consiste à utiliser des requêtes analytiques. Ça devrait ressembler a quelque chose comme ca:
Je soupçonne que vous pouvez vous débarrasser de la requête externe et la distinguer de l'intérieur, mais je ne suis pas sûr. En attendant, je sais que celui-ci fonctionne.
Si vous souhaitez en savoir plus sur les requêtes analytiques, je vous suggère de lire http://www.orafaq.com/node/55 et
http://www.akadia.com/services/ora_analytic_functions.html. Voici le court résumé.Sous le capot, les requêtes analytiques trient l'ensemble de données, puis le traitent séquentiellement. Au fur et à mesure que vous le traitez, vous partitionnez l'ensemble de données en fonction de certains critères, puis pour chaque ligne, une fenêtre s'affiche (par défaut, la première valeur de la partition correspond à la ligne actuelle - cette valeur par défaut est également la plus efficace) et vous pouvez calculer des valeurs à l'aide d'un nombre de fonctions analytiques (dont la liste est très similaire aux fonctions agrégées).
Dans ce cas, voici ce que fait la requête interne. L'ensemble de données est trié par UserId puis Date DESC. Ensuite, il le traite en un seul passage. Pour chaque ligne, vous retournez l'UserId et la première date vue pour cet UserId (puisque les dates sont triées DESC, c'est la date max). Cela vous donne votre réponse avec des lignes dupliquées. Ensuite, le DISTINCT externe écrase les doublons.
Ce n'est pas un exemple particulièrement spectaculaire de requêtes analytiques. Pour une victoire beaucoup plus importante, envisagez de prendre un tableau des reçus financiers et de calculer pour chaque utilisateur et reçu, un total cumulé de ce qu'ils ont payé. Les requêtes analytiques résolvent cela efficacement. D'autres solutions sont moins efficaces. C'est pourquoi ils font partie de la norme SQL 2003. (Malheureusement Postgres ne les a pas encore. Grrr ...)
la source
Une clause QUALIFY ne serait-elle pas à la fois la plus simple et la meilleure?
Pour le contexte, sur Teradata, ici, un test de taille décente s'exécute en 17s avec cette version QUALIFY et en 23s avec la `` vue en ligne '' / solution Aldridge # 1.
la source
rank()
fonction dans les situations où il y a des liens. Vous pourriez vous retrouver avec plus d'unrank=1
. Mieux vaut l'utiliserrow_number()
si vous ne voulez vraiment qu'un seul enregistrement retourné.QUALIFY
clause est spécifique à Teradata. Dans Oracle (au moins), vous devez imbriquer votre requête et filtrer à l'aide d'uneWHERE
clause sur l'instruction de sélection d'habillage (qui affecte probablement les performances d'une touche, j'imagine).Avec PostgreSQL 8.4 ou version ultérieure, vous pouvez utiliser ceci:
la source
Dans
Oracle 12c+
, vous pouvez utiliser les requêtes Top n avec la fonction analytiquerank
pour y parvenir de manière très concise sans sous-requêtes:Ce qui précède renvoie toutes les lignes avec max my_date par utilisateur.
Si vous ne voulez qu'une seule ligne avec la date maximale, remplacez-la
rank
parrow_number
:la source
Utilisez
ROW_NUMBER()
pour attribuer un classement unique par ordre décroissantDate
pour chacunUserId
, puis filtrez pour la première ligne pour chacunUserId
(c'est-à-direROW_NUMBER
= 1).la source
Je pense que vous devez faire cette variante de la requête précédente:
la source
la source
Juste eu à écrire un exemple "en direct" au travail :)
Celui-ci prend en charge plusieurs valeurs pour UserId à la même date.
Colonnes: UserId, Value, Date
Vous pouvez utiliser FIRST_VALUE au lieu de MAX et le rechercher dans le plan d'explication. Je n'ai pas eu le temps de jouer avec.
Bien sûr, si vous recherchez dans d'énormes tables, il est probablement préférable d'utiliser des indications complètes dans votre requête.
la source
la source
Je pense quelque chose comme ça. (Pardonnez-moi pour toute erreur de syntaxe; j'ai l'habitude d'utiliser HQL à ce stade!)
EDIT: Également mal lu la question! Correction de la requête ...
la source
(T-SQL) Obtenez d'abord tous les utilisateurs et leur maxdate. Rejoignez le tableau pour trouver les valeurs correspondantes pour les utilisateurs sur les maxdates.
résultats:
la source
La réponse ici est uniquement Oracle. Voici une réponse un peu plus sophistiquée dans tous les SQL:
Qui a le meilleur résultat global des devoirs (somme maximale des points de devoirs)?
Et un exemple plus difficile, qui a besoin d'explication, pour lequel je n'ai pas le temps atm:
Donnez le livre (ISBN et titre) qui est le plus populaire en 2008, c'est-à-dire qui est emprunté le plus souvent en 2008.
J'espère que cela aide (n'importe qui) .. :)
Cordialement, Guus
la source
En supposant que la date est unique pour un ID utilisateur donné, voici quelques TSQL:
la source
Je suis assez en retard pour la fête, mais le hack suivant surpassera les sous-requêtes corrélées et toute fonction d'analyse, mais a une restriction: les valeurs doivent être converties en chaînes. Cela fonctionne donc pour les dates, les nombres et autres chaînes. Le code n'a pas l'air bien mais le profil d'exécution est génial.
La raison pour laquelle ce code fonctionne si bien est qu'il n'a besoin d'analyser la table qu'une seule fois. Il ne nécessite aucun index et, surtout, il n'a pas besoin de trier la table, ce que font la plupart des fonctions d'analyse. Les index vous aideront si vous devez filtrer le résultat pour un seul ID utilisateur.
la source
Si vous utilisez Postgres, vous pouvez utiliser
array_agg
commeJe ne connais pas Oracle. C'est ce que j'ai trouvé
Les deux requêtes renvoient les mêmes résultats que la réponse acceptée. Voir SQLFiddles:
la source
À mon humble avis, cela fonctionne. HTH
la source
Je pense que cela devrait fonctionner?
la source
Essayez d'abord d'avoir mal lu la question, en suivant la première réponse, voici un exemple complet avec des résultats corrects:
-
-
la source
Cela prendra également en charge les doublons (retourner une ligne pour chaque user_id):
la source
Je viens de tester cela et cela semble fonctionner sur une table de journalisation
la source
Cela devrait être aussi simple que:
la source
Solution pour MySQL qui n'a pas de concepts de partition KEEP, DENSE_RANK.
Référence: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
la source
Si (UserID, Date) est unique, c'est-à-dire qu'aucune date n'apparaît deux fois pour le même utilisateur, alors:
la source
la source