Recupera la riga che ha il valore massimo per una colonna


575

Tavolo:

UserId, Value, Date.

Voglio ottenere UserId, Valore per il massimo (Data) per ogni UserId. Cioè, il valore per ciascun ID utente che ha la data più recente. C'è un modo per farlo semplicemente in SQL? (Preferibilmente Oracle)

Aggiornamento: mi scuso per qualsiasi ambiguità: ho bisogno di ottenere TUTTI gli UserId. Ma per ogni ID utente, solo quella riga in cui l'utente ha la data più recente.


21
Cosa succede se ci sono più righe con il valore di data massimo per un particolare ID utente?
David Aldridge,

Quali sono i campi chiave della tabella?
vamosrafa,

alcune soluzioni di seguito a confronto: sqlfiddle.com/#!4/6d4e81/1
Used_By_Al già

1
@DavidAldridge, Quella colonna è probabilmente unica.
Pacerier,

Risposte:


398

Ciò recupererà tutte le righe per le quali il valore della colonna my_date è uguale al valore massimo di my_date per quell'id utente. Ciò può recuperare più righe per l'id utente in cui la data massima è su più righe.

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

"Funzioni analitiche rock"

Modifica: per quanto riguarda il primo commento ...

"l'utilizzo di query analitiche e un self-join vanifica lo scopo delle query analitiche"

Non esiste un self-join in questo codice. Esiste invece un predicato posto sul risultato della vista inline che contiene la funzione analitica - una questione molto diversa e una pratica completamente standard.

"La finestra predefinita in Oracle è dalla prima riga nella partizione a quella corrente"

La clausola windowing è applicabile solo in presenza della clausola order by. Senza clausole order by, nessuna clausola windowing viene applicata per impostazione predefinita e nessuna può essere specificata in modo esplicito.

Il codice funziona.


39
Quando applicata a una tabella con 8,8 milioni di righe, questa query ha richiesto la metà del tempo delle query in alcune delle risposte più votate.
Derek Mahar,

4
Qualcuno ha cura di pubblicare un link all'equivalente MySQL di questo, se ce n'è uno?
redolent il

2
Questo ritorno non potrebbe duplicare? Per esempio. se due righe hanno lo stesso user_id e la stessa data (che risulta essere il massimo).
Jastr

2
@jastr Penso che sia stato riconosciuto nella domanda
David Aldridge,

3
Invece di MAX(...) OVER (...)te puoi anche usare ROW_NUMBER() OVER (...)(per il top-n-per-gruppo) o RANK() OVER (...)(per il più grande-n-per-gruppo).
MT0,

441

Vedo che molte persone usano subquery o funzionalità specifiche del fornitore per farlo, ma spesso faccio questo tipo di query senza subquery nel modo seguente. Utilizza un normale SQL standard, quindi dovrebbe funzionare con qualsiasi marca di RDBMS.

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;

In altre parole: recupera la riga da t1dove non esiste altra riga con la stessa UserIde una data maggiore.

(Ho messo l'identificatore "Date" nei delimitatori perché è una parola riservata SQL.)

In caso affermativo t1."Date" = t2."Date", appare il raddoppio. Di solito le tabelle hanno una auto_inc(seq)chiave, ad es id. Per evitare il raddoppio può essere usato come segue:

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 commento di @Farhan:

Ecco una spiegazione più dettagliata:

Un join esterno tenta di unirsi t1a t2. Per impostazione predefinita, t1vengono restituiti tutti i risultati di e, se è presente una corrispondenza t2, viene anche restituito. Se non esiste una corrispondenza t2per una determinata riga di t1, la query restituisce comunque la riga di t1e utilizza NULLcome segnaposto per tutte t2le colonne. Ecco come funzionano i join esterni in generale.

Il trucco di questa query è progettare la condizione di corrispondenza del join in modo tale che t2debba corrispondere alla stessa userid e ad una maggiore date . L'idea è se esiste una riga in t2cui ha una maggiore date, allora la riga t1è confrontata non può essere il più grande datedi quella userid. Ma se non vi è alcuna corrispondenza, ovvero se non esiste alcuna riga t2con un valore maggiore datedella riga in t1, sappiamo che la riga in t1era la riga con il massimo dateper il dato userid.

In quei casi (quando non c'è corrispondenza), le colonne di t2saranno NULL- anche le colonne specificate nella condizione di join. Quindi è per questo che usiamo WHERE t2.UserId IS NULL, perché stiamo cercando i casi in cui non è stata trovata alcuna riga con una maggiore dateper il dato userid.


7
Wow Bill. Questa è la soluzione più creativa a questo problema che ho visto. È anche abbastanza performante sul mio set di dati abbastanza grande. Questo sicuramente batte molte delle altre soluzioni che ho visto o i miei tentativi di risolvere questo dilemma.
Justin Noel,

37
Quando applicata a una tabella con 8,8 milioni di righe, questa query ha impiegato quasi il doppio di quella della risposta accettata.
Derek Mahar,

16
@Derek: le ottimizzazioni dipendono dal marchio e dalla versione di RDBMS, nonché dalla presenza di indici, tipi di dati, ecc. Appropriati
Bill Karwin,

7
Su MySQL, questo tipo di query sembra effettivamente causarne il loop sul risultato di un join cartesiano tra le tabelle, con conseguente tempo O (n ^ 2). L'utilizzo del metodo subquery ha invece ridotto il tempo di query da 2,0 a 0,003 secondi. YMMV.
Jesse,

1
C'è un modo per adattare questo per abbinare le righe in cui la data è la data più grande minore o uguale a una data specificata dall'utente? Ad esempio, se l'utente specifica la data "23-OCT-2011" e la tabella include le righe per "24-OCT-2011", "22-OCT-2011", "20-OCT-2011", allora desidero ottieni "22-OTT-2011". Mi gratto la testa e leggo questo frammento da un po 'di tempo ...
Cory Kendall

164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

3
Nei miei test usando una tabella con un gran numero di righe, questa soluzione ha impiegato circa il doppio di quella nella risposta accettata.
Derek Mahar,

7
Mostra il tuo test, per favore
Rob van Wijk

Confermo che è molto più veloce di altre soluzioni
tamersalama,

5
il problema è che non restituisce il record completo
Used_By_Alla

@ user2067753 No, non restituisce il record completo. Puoi usare la stessa espressione MAX () .. KEEP .. su più colonne, in modo da poter selezionare tutte le colonne necessarie. Ma è scomodo se vuoi un gran numero di colonne e preferisci usare SELECT *.
Dave Costa,

51

Non conosco i nomi esatti delle tue colonne, ma sarebbe qualcosa del genere:

    seleziona userid, valore
      dagli utenti u1
     dove data = (seleziona max (data)
                     dagli utenti u2
                    dove u1.userid = u2.userid)

3
Probabilmente non molto efficace, Steve.
David Aldridge,

7
Probabilmente stai sottovalutando Oracle Query Optimizer.
Rafał Dowgird,

3
Affatto. Questo sarà quasi certamente implementato come una scansione completa con un join loop annidato per ottenere le date. Stai parlando di io logici nell'ordine di 4 volte il numero di righe nella tabella ed essere spaventoso per quantità di dati non banali.
David Aldridge,

4
Cordiali saluti, "Non efficiente, ma funziona" è lo stesso di "Funziona, ma non è efficiente". Quando abbiamo rinunciato all'efficienza come obiettivo di progettazione?
David Aldridge,

6
+1 perché quando i tuoi database non sono lunghi milioni di righe ogni giorno, questa è la soluzione più facilmente comprensibile. quando hai più sviluppatori di tutti i livelli di abilità che modificano il codice, la comprensibilità è più importante di una frazione di secondo nelle prestazioni che è inosservabile.
n00b,

35

Non essendo al lavoro, non ho Oracle a portata di mano, ma mi sembra di ricordare che Oracle consente di abbinare più colonne in una clausola IN, il che dovrebbe almeno evitare le opzioni che utilizzano una subquery correlata, che raramente è una buona idea.

Qualcosa del genere, forse (non ricordo se l'elenco delle colonne deve essere tra parentesi o meno):

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

EDIT: appena provato per davvero:

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

Quindi funziona, anche se alcune delle cose nuove di zanna menzionate altrove potrebbero essere più performanti.


4
Funziona bene anche su PostgreSQL. E mi piace la semplicità e la generalità di esso - la sottoquery dice "Ecco i miei criteri", la query esterna dice "Ed ecco i dettagli che voglio vedere". +1.
j_random_hacker,

13

So che hai richiesto Oracle, ma in SQL 2005 ora utilizziamo questo:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

7

Non ho Oracle per testarlo, ma la soluzione più efficiente è utilizzare query analitiche. Dovrebbe assomigliare a qualcosa di simile a questo:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

Ho il sospetto che tu possa sbarazzarti della query esterna e metterti distinto all'interno, ma non sono sicuro. Nel frattempo so che funziona.

Se vuoi conoscere le query analitiche, ti suggerisco di leggere http://www.orafaq.com/node/55 e http://www.akadia.com/services/ora_analytic_functions.html . Ecco il breve riassunto.

Sotto le query analitiche del cofano, ordinare l'intero set di dati, quindi elaborarlo in sequenza. Durante l'elaborazione, il set di dati viene partizionato in base a determinati criteri, quindi per ogni riga viene visualizzata una finestra (il valore predefinito è il primo valore nella partizione alla riga corrente - tale valore predefinito è anche il più efficiente) e può calcolare i valori utilizzando un numero di funzioni analitiche (il cui elenco è molto simile alle funzioni aggregate).

In questo caso, ecco cosa fa la query interna. L'intero set di dati è ordinato per ID utente quindi Data DESC. Quindi lo elabora in un unico passaggio. Per ogni riga viene restituito l'Idutente e la prima Data visualizzata per tale ID utente (poiché le date sono ordinate DESC, questa è la data massima). Questo ti dà la tua risposta con righe duplicate. Quindi il DISTINCT esterno schiaccia i duplicati.

Questo non è un esempio particolarmente spettacolare di query analitiche. Per una vittoria molto più grande prendi in considerazione la possibilità di prendere una tabella delle entrate finanziarie e di calcolare per ogni utente e ricevuta, un totale parziale di ciò che hanno pagato. Le query analitiche lo risolvono in modo efficiente. Altre soluzioni sono meno efficienti. Ecco perché fanno parte dello standard SQL del 2003. (Purtroppo Postgres non li ha ancora. Grrr ...)


È inoltre necessario restituire il valore della data per rispondere completamente alla domanda. Se ciò significa un'altra clausola first_value, suggerirei che la soluzione sia più complessa di quanto dovrebbe essere e che il metodo analitico basato su max (data) sia migliore.
David Aldridge,

La dichiarazione della domanda non dice nulla sulla restituzione della data. Puoi farlo aggiungendo un'altra PRIMA (Data) oppure semplicemente interrogando la Data e modificando la query esterna in GROUP BY. Userei il primo e mi aspetto che l'ottimizzatore calcoli entrambi in un passaggio.
user11318,

"La dichiarazione della domanda non dice nulla sulla restituzione della data" ... sì, hai ragione. Scusate. Ma l'aggiunta di più clausole FIRST_VALUE diventerebbe disordinata abbastanza rapidamente. È un ordinamento a finestra singola, ma se avessi 20 colonne da restituire per quella riga, hai scritto un sacco di codice per guadare.
David Aldridge,

Mi viene anche in mente che questa soluzione non è deterministica per i dati in cui un singolo userid ha più righe che hanno la data massima e diversi VALUE. Più un difetto nella domanda che la risposta però.
David Aldridge,

1
Sono d'accordo che è dolorosamente prolisso. Tuttavia, non è generalmente il caso di SQL? E hai ragione sul fatto che la soluzione non è deterministica. Esistono diversi modi per gestire i legami, e talvolta ognuno è quello che desideri.
user11318,

6

Una clausola QUALIFY non sarebbe la più semplice e la migliore?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

Per il contesto, su Teradata qui un test di dimensioni decenti di questo viene eseguito in 17 secondi con questa versione QUALIFY e in 23 secondi con la "vista in linea" / soluzione Aldridge n. 1.


1
Questa è la migliore risposta secondo me. Tuttavia, prestare attenzione alla rank()funzione in situazioni in cui vi sono legami. Potresti finire con più di uno rank=1. Meglio usare row_number()se vuoi davvero che venga restituito solo un record.
cartbeforehorse,

1
Inoltre, tieni presente che la QUALIFYclausola è specifica di Teradata. In Oracle (almeno) devi annidare la tua query e filtrare usando una WHEREclausola sull'istruzione wrapping select (che probabilmente colpisce le prestazioni un tocco, immagino).
cartbeforehorse,

5

In Oracle 12c+, è possibile utilizzare le Top n query insieme alla funzione analitica rankper ottenere questo in modo molto conciso senza subquery:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

Quanto sopra restituisce tutte le righe con max my_date per utente.

Se desideri solo una riga con la data massima, sostituisci rankcon row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

5

Utilizzare ROW_NUMBER()per assegnare una classifica unica in ordine decrescente Dateper ciascuno UserId, quindi filtrare alla prima riga per ciascuno UserId(ovvero, ROW_NUMBER= 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;

5

Con PostgreSQL 8.4 o successivo, puoi usare questo:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

3

penso che tu faccia questa variante alla query precedente:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

3
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

3

Ho dovuto scrivere un esempio "live" al lavoro :)

Questo supporta più valori per UserId nella stessa data.

Colonne: ID utente, valore, data

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

Puoi utilizzare FIRST_VALUE invece di MAX e cercarlo nel piano esplicativo. Non ho avuto il tempo di giocarci.

Ovviamente, se cerchi in tabelle enormi, probabilmente è meglio se usi suggerimenti FULL nella tua query.


3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

2

Penso a qualcosa del genere. (Perdonami per eventuali errori di sintassi; sono abituato a usare HQL a questo punto!)

EDIT: anche frainteso la domanda! Correzione della query ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

Non soddisfa la condizione "per ogni ID utente"
David Aldridge,

Dove fallirebbe? Per ogni ID utente negli utenti, verrà garantito che almeno una riga contenente tale ID utente verrà restituita. O mi sto perdendo un caso speciale da qualche parte?
jdmichal,

2

(T-SQL) Prima ottenere tutti gli utenti e il loro maxdate. Unisciti alla tabella per trovare i valori corrispondenti per gli utenti sui maxdate.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

i risultati:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

2

La risposta qui è solo Oracle. Ecco una risposta un po 'più sofisticata in tutto l'SQL:

Chi ha il miglior risultato complessivo per i compiti (somma massima dei punti per i compiti)?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

E un esempio più difficile, che ha bisogno di qualche spiegazione, per il quale non ho tempo atm:

Dai il libro (codice ISBN e titolo) più popolare nel 2008, ovvero preso in prestito più spesso nel 2008.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

Spero che questo aiuti (chiunque) .. :)

Saluti, Guus


La risposta accettata non è "solo Oracle" - è SQL standard (supportato da molti DBMS)
a_horse_with_no_name

2

Supponendo che la data sia unica per un determinato ID utente, ecco alcuni TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

2

Sono in ritardo alla festa, ma il seguente hack supererà sia le subquery correlate sia qualsiasi funzione di analisi ma ha una limitazione: i valori devono essere convertiti in stringhe. Quindi funziona per date, numeri e altre stringhe. Il codice non ha un bell'aspetto ma il profilo di esecuzione è eccezionale.

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

Il motivo per cui questo codice funziona così bene è che deve solo scansionare la tabella una volta. Non richiede alcun indice e, soprattutto, non è necessario ordinare la tabella, come fanno la maggior parte delle funzioni di analisi. Gli indici ti aiuteranno se hai bisogno di filtrare il risultato per un singolo userid.


È un buon piano di esecuzione rispetto alla maggior parte, ma applicare tutti quei trucchi a più di un paio di campi sarebbe noioso e potrebbe funzionare contro di esso. Ma molto interessante - grazie. vedi sqlfiddle.com/#!4/2749b5/23
Used_By_Al già

Hai ragione, può diventare noioso, motivo per cui questo dovrebbe essere fatto solo quando le prestazioni della query lo richiedono. Questo è spesso il caso degli script ETL.
aLevelOfIndirection

questo è molto carino. fatto qualcosa di simile usando LISTAGG ma sembra brutto. postgres ha un altenativo migliore usando array_agg. vedi la mia risposta :)
Bruno Calza,

1
select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO questo funziona. HTH


1

Penso che dovrebbe funzionare?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

1

Per prima cosa ho letto male la domanda, seguendo la risposta migliore, ecco un esempio completo con risultati corretti:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

1

Questo si occuperà anche dei duplicati (restituisce una riga per ogni user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

1

Ho appena provato questo e sembra funzionare su una tabella di registrazione

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

1

Questo dovrebbe essere semplice come:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

1

Soluzione per MySQL che non ha concetti di partizione KEEP, DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

Riferimento: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html


Questo non funziona "anche su altri DB ". Funziona solo su MySQL e possibilmente su SQL Server perché ha un concetto simile di variabili. Non funzionerà sicuramente su Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Inoltre la risposta accettata è ANSI SQL standard (che di fatto solo MySQL non supporta)
a_horse_with_no_name

cavallo, immagino tu abbia ragione. Non ho conoscenza di altri DB o ANSI. La mia soluzione è in grado di risolvere il problema in MySQL, che non ha il supporto adeguato per ANSI SQL per risolverlo in modo standard.
Ben Lin,

1

Se stai usando Postgres, puoi usare array_agglike

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

Non ho familiarità con Oracle. Questo è quello che mi è venuto in mente

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

Entrambe le query restituiscono gli stessi risultati della risposta accettata. Vedi SQLFiddles:

  1. Risposta accettata
  2. La mia soluzione con Postgres
  3. La mia soluzione con Oracle

0

Se (UserID, Date) è univoco, ovvero nessuna data appare due volte per lo stesso utente, allora:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;

Credo che anche tu debba unirti a UserID
Tom H,

0
select   UserId,max(Date) over (partition by UserId) value from users;

2
Ciò restituirà tutte le righe, non solo una riga per utente.
Jon Heller,
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.