2009-06-18 9 views
6

Ce que j'ai est fondamentalement un problème qui est facilement résolu avec plusieurs tables, mais je n'ai qu'une seule table pour le faire.SQL - SELECT MAX() et le champ d'accompagnement

Tenir compte de la table de base de données suivante

UserID UserName EmailAddress   Source 
3K3S9 Ben  [email protected]  user 
SF13F Harry [email protected] 3rd_party 
SF13F Harry [email protected] user 
76DSA Lisa  [email protected]  user 
OL39F Nick  [email protected] 3rd_party 
8F66S Stan  [email protected]  user 

je dois sélectionner tous les champs, mais seulement qui chaque utilisateur une fois avec l'un de leurs adresses e-mail (le « plus grand » un tel que déterminé par la fonction MAX()). Ceci est le résultat que je suis après ...

UserID UserName EmailAddress   Source 
3K3S9 Ben  [email protected]  user 
SF13F Harry [email protected] 3rd_party 
76DSA Lisa  [email protected]  user 
OL39F Nick  [email protected] 3rd_party 
8F66S Stan  [email protected]  user 

Comme vous pouvez le voir, « Harry » n'apparaît qu'une fois avec son « plus haut » adresse e-mail la « source » correcponding

Actuellement ce qui se passe est que nous sommes en train de regrouper sur UserID, UserName et en utilisant MAX() pour EmailAddress et Source, mais le maximum de ces deux champs ne correspond pas toujours, ils doivent provenir du même enregistrement.

J'ai essayé un autre processus en rejoignant la table avec lui-même, mais j'ai seulement réussi à obtenir l'adresse email correcte mais pas la "source" correspondante pour cette adresse.

Toute aide serait appréciée comme je l'ai passé beaucoup trop de temps à essayer de résoudre ce déjà :)

+0

Avez-vous une colonne supplémentaire avec une clé primaire définie? La paire (UserId, EmailAddress) est-elle unique? –

Répondre

7

Si vous êtes sur SQL Server 2005 ou supérieur,

SELECT UserID, UserName, EmailAddress, Source 
FROM (SELECT UserID, UserName, EmailAddress, Source, 
       ROW_NUMBER() OVER (PARTITION BY UserID 
            ORDER BY EmailAddress DESC) 
        AS RowNumber 
     FROM MyTable) AS a 
WHERE a.RowNumber = 1 

Bien sûr il existe des moyens de faire la même tâche sans les fonctions de classement (SQL-Standard) telles que ROW_NUMBER, que SQL Server a implémenté uniquement depuis 2005 - y compris les requêtes dépendantes imbriquées et les jointures à gauche avec ON, y compris un '>' et un WHERE ... IS NULL truc - mais les fonctions de classement rendent le code lisible et (en théorie) bien optimisable par le moteur SQL Server.

Edit: this article est un tutoriel bien sur le classement, mais il utilise RANK dans les exemples au lieu de ROW_NUMBER (ou autre fonction de classement, DENSE_RANK) - la distinction importe quand il y a des « liens » entre les lignes groupées dans la même partition selon les critères de commande. this post fait un bon travail expliquant la différence.

+0

Très intéressant Alex, je vais étudier à propos de ces fonctionnalités. – tekBlues

+0

Cela fonctionne certainement très bien ... mais je ne comprends pas la syntaxe>. Nippysaurus

+0

Édité ma réponse pour ajouter des URL à deux bons, courts tutoriels sur les fonctions de classement - HTH! –

5
select distinct * from table t1 
where EmailAddress = 
(select max(EmailAddress) from table t2 
where t1.userId = t2.userId) 
+0

Il est à noter que cela peut souvent fonctionner plus vite que la réponse acceptée, surtout s'il y a un index sur {userid, EmailAddress DESC} sur t2 –

0
select distinct 
    * 
from  
    SomeTable a 
inner join (
    select max(emailAddress), userId 
    from 
    SomeTable 
    group by 
    userId 
) b on a.emailAddress = b.emailAddress and a.userId = b.userId 
+0

Je serais plus heureux si la condition ON comprend a.userID = b.userID ainsi que l'adresse e-mail. –

+0

Vrai, cela le rend plus spécifique et évite les problèmes potentiels. J'ai édité ma réponse pour refléter cela. –

0

Je pense avoir une solution qui est différente de celles déjà proposées:

 
select * 
from foo 
where id = (
    select id 
    from foo F 
    where F.bar = foo.bar 
    order by F.baz 
    limit 1 
) 

Cela vous donne tous les enregistrements foo qui ont le plus baz par rapport à d'autres enregistrements foo avec le même bar.