come faccio a richiedere sql per una data di registrazione più recente per ogni utente


228

Ho una tabella che è una raccolta voci su quando un utente era connesso.

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

Come faccio a creare una query che mi darebbe la data più recente per ciascun utente?

Aggiornamento: ho dimenticato che avevo bisogno di avere un valore che corrisponda all'ultima data.


7
che database stai usando? MySQL, SQL Server, Oracle, ...?
Peter Lang

1
Hai bisogno del valore che corrisponde alla data più recente o al valore massimo E alla data massima?
Matthew Jones,

Risposte:


381
select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

3
Quando si lavora con postgresql questa versione sarebbe più veloce dell'uso di un IN (sottoquery) invece del join interno?
TheOne

3
@TheOne come esperienza, l'uso del join interno è più veloce che in condizioni
dada,

14
Attento con questo approccio: può restituire più di una riga per utente se hanno più di un record per data ( max(date)restituirebbe una data che si unirebbe a più record). Per evitare questo problema, sarebbe preferibile utilizzare @ soluzione dotjoe: stackoverflow.com/a/2411763/4406793 .
Marco Roy,

@RedFilter Questo ha funzionato perfettamente per il mio problema. Grazie mille per una domanda così tecnica. A proposito, ho usato datetime invece di data per evitare di ottenere più risultati per una data particolare
Muhammad Khan,

perché è necessario che il raggruppamento 'and t.date = tm.MaxDate' non sia sufficiente?
Duldi,

125

Utilizzo delle funzioni della finestra (funziona in Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

1
Vale la pena chiarire quale prodotto / versione Sybase. Non funziona su Sybase ASE 16.
levant pied

2
Un grande vantaggio di questo approccio è che è garantito che restituisca sempre solo una riga per partizione ( username, in questo caso) e non richiede nemmeno un campo unico "ordinabile" (come unirsi max(date)ad altre risposte).
Marco Roy,

1
Solo per aggiungere qualcosa a ciò che ha detto @MarcoRoy, se ti capita di avere più di un record con la stessa data massima, se cambi la query, come quando stai eseguendo il debug, un record diverso può ricevere un numero di riga 1, quindi i risultati potrebbero essere incoerenti. Ma finché non ti interessa davvero, questo non dovrebbe essere un problema. Questo può essere risolto se aggiungi il PK dopo la data. Ad esempio: order by date desc, id desc).
Andrew,

40

Vedo che la maggior parte degli sviluppatori utilizza una query inline senza considerare il suo impatto su enormi dati.

Semplicemente, puoi farlo tramite:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

3
in realtà questo funziona solo per i duplicati, se hai più di 2 valori, la condizione a.date <b.date non funziona, il che significa che non è una soluzione generale, sebbene l'idea di lavorare con LEFT OUTER JOIN sia importante cosa in questa risposta.
iversoncru,

È interessante notare che Sybase ASE 16 funziona bene per tabelle più piccole (<10k righe), ma con quelle più grandi (> 100k righe) si blocca ... Ho pensato che questo sarebbe l'esempio perfetto che i DB relazionali dovrebbero eccellere in ...
levant pied

1
@levantpied ... Sì, il join sinistro è costoso su set di dati più grandi. È possibile modificare una performance inserendo la condizione del filtro sul join stesso per gestirla in qualche modo, se possibile.
sujeet il

21

Per ottenere l'intera riga contenente la data massima per l'utente:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

1
Lavorare per MySQL
School Boy il

1
Attenzione che questo ti darà duplicati se c'è più di un record con la stessa data per un utente specifico. Potresti o meno volerlo.
Andrew,

Questo sql è lento in Oracle con la clausola in, non utilizzerà l'indice
meadlai

9
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

4
Mentre questa è un'altra possibile soluzione, questo non è normalmente un buon modo per risolverlo. In questo modo la query interna verrà eseguita una volta per ogni nome nella tabella, causando un notevole rallentamento per qualsiasi tabella di dimensioni significative. Fare una query separata che non ha un elemento dalla prima query nella clausola where, quindi avere le due tabelle unite, di solito sarà più veloce.
Scott Chamberlain,

Questo ha la bella caratteristica di essere una delle soluzioni più comprensibili che non sono specifiche dell'implementazione.
Michael Szczepaniak,

7

Dalla mia esperienza, il modo più veloce è quello di prendere ogni riga per la quale non ci sono più nuove righe nella tabella.

Un altro vantaggio è che la sintassi utilizzata è molto semplice e che il significato della query è piuttosto semplice da comprendere (prendere tutte le righe in modo tale che non esista alcuna riga più recente per il nome utente considerato).

NON ESISTE

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

ROW_NUMBER

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

UNIONE INTERNA

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

SINISTRA ESTERNO UNISCITI

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

Ho difficoltà a comprendere la versione di NOT EXISTS. Non ti manca un'aggregazione nella parte della sottoquery? Se eseguo questo sul mio tavolo, ottengo solo 3 record di dipendenti da 40 dipendenti che ho nella tabella. Dovrei ottenere almeno 40 registrazioni. Nella query interna, non dovremmo corrispondere anche per nome utente?
Narshe,

Funziona per me usando il seguente:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Narshe

Ho esaminato NOT EXISTS e sembra restituire solo la voce più alta per tutti gli utenti anziché: "una query che mi darebbe la data più recente per ogni utente".
Tasos Zervos,

Hai proprio ragione, aggiorno la mia domanda. Grazie per la tua osservazione! @Narshe scusa ho perso i tuoi commenti per qualche motivo: / Ma hai assolutamente ragione.
Fabian Pijcke,

2

Questo dovrebbe darti il ​​risultato corretto per la tua domanda modificata.

La query secondaria si assicura di trovare solo le righe dell'ultima data e l'esterno GROUP BYsi occuperà dei legami. Quando sono presenti due voci per la stessa data per lo stesso utente, verrà restituita quella con il valore più alto value.

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

1

È inoltre possibile utilizzare la funzione di classificazione analitica

    with temp as 
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1

0
SELECT Username, date, value
 from MyTable mt
 inner join (select username, max(date) date
              from MyTable
              group by username) sub
  on sub.username = mt.username
   and sub.date = mt.date

Affronterebbe il problema aggiornato. Potrebbe non funzionare così bene su tabelle di grandi dimensioni, anche con una buona indicizzazione.


0
SELECT *
FROM ReportStatus c
inner join ( SELECT 
  MAX(Date) AS MaxDate
  FROM ReportStatus ) m
on  c.date = m.maxdate

0

Per Oracle ordina il set di risultati in ordine decrescente e prende il primo record, quindi otterrai l'ultimo record:

select * from mytable
where rownum = 1
order by date desc

0
SELECT DISTINCT Username, Dates,value 
FROM TableName
WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)


Username    Dates       value
bob         2010-02-02  1.2       
brad        2010-01-02  1.1       
fred        2010-01-03  1.0       

Questo probabilmente non funzionerebbe se più utenti avessero ordini nella stessa data; e se Brad e Bob avessero entrambi un ordine il 2 gennaio?
AHiggins,

Sto raggruppando per nome utente in modo che funzioni e i risultati saranno così: Nome utente Date valore bob 2010-02-02 1.2 brad 2010-02-02 1.4 fred 2010-01-03 1.0
wara

0
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
            FROM MyTable
            GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date

4
Una o due frasi sull'attuazione o sulla spiegazione sono molto utili per creare una risposta di qualità.

0

Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

Query interna restituirà la data più recente per l'utente corrente, la query esterna estrarrà tutti i dati in base al risultato della query interna.


0

Ho usato questo modo per prendere l'ultimo record per ogni utente che ho sul mio tavolo. Era una query per ottenere l'ultima posizione per il venditore come recentemente rilevato sui dispositivi PDA.

CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE()) 
Group By GS.UserID
GO
select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
        from USERGPS gs
        inner join USER s on gs.SalesManNo = s.SalesmanNo 
        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
        order by LastDate desc

0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)

Benvenuto in StackOverflow e grazie per aver tentato di aiutarti. Le risposte di solo codice come le tue sono meno apprezzate rispetto alle risposte che spiegano la soluzione.
Yunnosch,

Si prega di leggere questo how-to-risposta per la fornitura di risposta di qualità.
thewaywewere

e. non ritorna su MAX per ogni nome utente, solo sull'ultima riga singola.
IrvineCAGuy,

0

La mia piccola compilation

  • auto joinmeglio di nidificatoselect
  • ma group bynon ti dà ciò per primary keycui è preferibilejoin
  • questa chiave può essere fornita partition byinsieme a first_value( docs )

Quindi, ecco una query:

Selezionare
 t. *
a partire dal 
 Tabella t join interno (
  selezionare distinto first_value (ID) su (partizione per ordine GroupColumn per descrizione DateColumn) come ID
  dalla tabella
  dove FilterColumn = 'value'
 ) j su t.ID = j.ID

Professionisti:

  • Filtra i dati con whereistruzione usando qualsiasi colonna
  • select eventuali colonne da righe filtrate

Contro:

  • È necessario MS SQL Server a partire dal 2012.

0

Ho fatto un po 'per la mia applicazione in quanto:

Di seguito è la query:

select distinct i.userId,i.statusCheck, l.userName from internetstatus 
as i inner join login as l on i.userID=l.userID 
where nowtime in((select max(nowtime) from InternetStatus group by userID));    

0

Questo è simile a una delle risposte sopra, ma secondo me è molto più semplice e ordinato. Inoltre, mostra un buon uso dell'istruzione cross apply. Per SQL Server 2005 e versioni successive ...

select
    a.username,
    a.date,
    a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate

0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 

1
Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente comporterebbe più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni. Dalla recensione
Doppio segnale acustico

-2

Questo dovrebbe funzionare anche al fine di ottenere tutte le ultime voci per gli utenti.

SELECT username, MAX(date) as Date, value
FROM MyTable
GROUP BY username, value

1
Salve, la colonna dei valori deve essere inclusa nella clausola group by.
Juan Ruiz de Castilla,

-4

Dovresti usare la funzione aggregata MAX e GROUP BY

SELECT username, MAX(date), value FROM tablename GROUP BY username, value

7
La tua modifica sceglierà solo una casuale value, non quella associata alla MAX(date)riga.
Alison R.

fornirà la data massima ma il nome utente e il valore potrebbero non essere dello stesso record.
SKR
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.