2009-06-18 13 views
6

Quello che ho è fondamentalmente un problema che è facilmente risolvibile con più tabelle, ma ho solo una singola tabella per farlo.SQL - SELEZIONA MAX() e relativo campo

Si consideri il seguente tabella del database

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 

ho bisogno di selezionare tutti i campi, ma solo che ogni utente una volta insieme a uno dei loro indirizzi di posta elettronica (il "più grande" uno come determinato dal MAX (funzione)). Questo è il risultato sono dopo ...

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 

Come si può vedere, "Harry" è solo dimostrato ancora una volta con il suo indirizzo di posta elettronica "più alto" del correcponding "fonte"

Attualmente ciò che sta accadendo è che stiamo raggruppando su UserID, UserName e usando MAX() per EmailAddress e Source, ma il massimo di quei due campi non corrisponde sempre, devono essere dallo stesso record.

Ho provato un altro processo unendo la tabella con se stesso, ma sono riuscito a ottenere solo l'indirizzo email corretto ma non la "fonte" corrispondente per quell'indirizzo.

Qualsiasi aiuto sarebbe apprezzato come ho speso troppo tempo cercando di risolvere questo già :)

+0

Avete una colonna aggiuntiva con una chiave primaria definita? La coppia (UserId, EmailAddress) è unica? –

risposta

7

Se siete su SQL Server 2005 o superiore,

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 

Naturalmente ci sono modi per fare lo stesso compito senza le funzioni di classifica (SQL-Standard) come ROW_NUMBER, che SQL Server ha implementato solo dal 2005, comprese le query dipendenti nidificate e i join self left con uno ON incluso un '>' e un WHERE ... IS NULL: ma le funzioni di classifica rendono il codice leggibile e (in teoria) ottimamente ottimizzato da SQL Server Engine.

Edit: this article è un bel tutorial sul posizionamento, ma utilizza RANK negli esempi invece di ROW_NUMBER (o l'altra funzione di rango, DENSE_RANK) - la distinzione conta quando ci sono "legami" tra le righe raggruppate nello stesso partizione in base ai criteri di ordinazione. this post fa un buon lavoro spiegando la differenza.

+0

Alex molto interessante, studierò su queste caratteristiche. – tekBlues

+0

Questo sicuramente funziona molto bene ... eppure non capisco la sintassi>. Nippysaurus

+0

Modificato la mia risposta per aggiungere URL a due buoni, brevi tutorial sulle funzioni di classificazione - HTH! –

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

Vale la pena notare che questo spesso può eseguire più velocemente della risposta accettata, specialmente se c'è un indice su {userid, EmailAddress DESC} su 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

Sarei più felice se la condizione ON includesse a.userID = b.userID e l'indirizzo email. –

+0

È vero, lo rende più specifico ed evita potenziali problemi. Ho modificato la mia risposta per riflettere questo. –

0

Penso di avere una soluzione che sia diversa da quelle già proposte:

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

Questo ti dà tutti i record Foo che hanno il maggior baz rispetto ad altri record Foo con lo stesso bar.

Problemi correlati