Buscar a linha que possui o valor Máximo para uma coluna

575

Mesa:

UserId, Value, Date.

Quero obter o UserId, Value para o máximo (Data) de cada UserId. Ou seja, o valor para cada UserId que possui a data mais recente. Existe uma maneira de fazer isso simplesmente no SQL? (Preferencialmente Oracle)

Atualização: desculpas por qualquer ambiguidade: preciso obter TODOS os UserIds. Mas para cada UserId, apenas a linha em que esse usuário tem a data mais recente.

Umang
fonte
21
E se houver várias linhas com o valor máximo de data para um ID do usuário específico?
David Aldridge
Quais são os principais campos da tabela?
VamosJO
algumas soluções abaixo comparadas: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already
1
@ DavidAldridge, essa coluna é provavelmente única.
Pacerier 03/02

Respostas:

398

Isso recuperará todas as linhas para as quais o valor da coluna my_date é igual ao valor máximo de my_date para esse ID do usuário. Isso pode recuperar várias linhas para o ID do usuário em que a data máxima está em várias linhas.

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

"Funções analíticas rock"

Edit: Com relação ao primeiro comentário ...

"o uso de consultas analíticas e uma associação automática anulam o objetivo das consultas analíticas"

Não há auto-junção neste código. Em vez disso, existe um predicado colocado no resultado da exibição em linha que contém a função analítica - uma questão muito diferente e uma prática completamente padrão.

"A janela padrão no Oracle é da primeira linha da partição até a atual"

A cláusula de janelas é aplicável apenas na presença da ordem por cláusula. Sem ordem por cláusula, nenhuma cláusula de janelas é aplicada por padrão e nenhuma pode ser especificada explicitamente.

O código funciona.

David Aldridge
fonte
39
Quando aplicada a uma tabela com 8,8 milhões de linhas, essa consulta levou metade do tempo das consultas em algumas das outras respostas altamente votadas.
Derek Mahar
4
Alguém gostaria de postar um link para o MySQL equivalente a isso, se houver um?
redolent 10/01
2
Não foi possível esse retorno duplicar? Por exemplo. se duas linhas tiverem o mesmo user_id e a mesma data (que é o máximo).
jastr
2
@jastr Eu acho que isso foi reconhecido na pergunta #
David Aldridge
3
Em vez de MAX(...) OVER (...)você também pode usar ROW_NUMBER() OVER (...)(para o principal n por grupo) ou RANK() OVER (...)(para o maior n por grupo).
MT0
441

Vejo muitas pessoas usarem subconsultas ou outros recursos específicos do fornecedor para fazer isso, mas geralmente faço esse tipo de consulta sem subconsultas da seguinte maneira. Ele usa SQL simples e padrão, portanto, deve funcionar em qualquer marca de 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;

Em outras palavras: busque a linha de t1onde não existe outra linha com a mesma data UserIde uma data maior.

(Coloquei o identificador "Data" nos delimitadores porque é uma palavra reservada ao SQL.)

Caso isso t1."Date" = t2."Date"ocorra, a duplicação será exibida. Normalmente, as tabelas têm auto_inc(seq)chave, por exemplo id. Para evitar a duplicação pode ser usado a seguir:

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 comentário de @Farhan:

Aqui está uma explicação mais detalhada:

Uma junção externa tentativas para se juntar t1com t2. Por padrão, todos os resultados de t1são retornados e, se houver uma correspondência t2, ele também será retornado. Se não houver correspondência t2para uma determinada linha de t1, a consulta ainda retornará a linha de t1e será usada NULLcomo espaço reservado para todas t2as colunas de. É assim que as junções externas funcionam em geral.

O truque nesta consulta é projetar a condição de correspondência da junção, que t2deve corresponder à mesma userid e maior date . A ideia é que, se existir uma linha t2com uma maior date, então a linha na qual t1ela é comparada não pode ser a melhor datepara isso userid. Mas se não houver correspondência - ou seja, se nenhuma linha existir t2com uma maior dateque a linha exibida t1-, sabemos que a linha in t1foi a linha com a maior datepara o dado userid.

Nesses casos (quando não há correspondência), as colunas t2serão NULL- mesmo as colunas especificadas na condição de junção. É por isso que usamos WHERE t2.UserId IS NULL, porque estamos pesquisando os casos em que nenhuma linha foi encontrada com uma maior datepara o dado userid.

Bill Karwin
fonte
7
Uau Bill. Esta é a solução mais criativa para esse problema que já vi. Também tem um bom desempenho no meu conjunto de dados bastante grande. Isso com certeza supera muitas das outras soluções que eu já vi ou minhas próprias tentativas de resolver esse dilema.
Justin Noel
37
Quando aplicada a uma tabela com 8,8 milhões de linhas, essa consulta demorou quase o dobro do tempo da resposta aceita.
Derek Mahar
16
@Derek: Optimizations depender da marca e versão do RDBMS, bem como presença de índices apropriados, tipos de dados, etc.
Bill Karwin
7
No MySQL, esse tipo de consulta parece realmente fazer com que ele faça um loop sobre o resultado de uma junção cartesiana entre as tabelas, resultando em tempo O (n ^ 2). O uso do método de subconsulta reduziu o tempo de consulta de 2,0s para 0,003s. YMMV.
Jesse
1
Existe uma maneira de adaptar isso para corresponder às linhas em que data é a maior data menor ou igual a uma data especificada pelo usuário? Por exemplo, se o usuário fornecer a data "23 de outubro de 2011" e a tabela incluir linhas para "24 de outubro de 2011", "22 de outubro de 2011", "20 de outubro de 2011", eu quero obtenha "22 de outubro de 2011". Sido coçar a cabeça e ler esse trecho por um tempo agora ...
Cory Kendall
164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
Dave Costa
fonte
3
Nos meus testes usando uma tabela com um grande número de linhas, essa solução demorou cerca de duas vezes o tempo da resposta aceita.
Drek Mahar
7
Mostre seu teste, por favor
Rob van Wijk
Confirmo que é muito mais rápido do que outras soluções
tamersalama
5
problema é que ele não retorna o registro completo
Used_By_Already
@ user2067753 Não, ele não retorna o registro completo. Você pode usar a mesma expressão MAX () .. KEEP .. em várias colunas, para selecionar todas as colunas necessárias. Mas é inconveniente se você deseja um grande número de colunas e prefere usar SELECT *.
Dave Costa
51

Não sei o nome exato das colunas, mas seria algo como isto:

    selecione ID do usuário, valor
      dos usuários u1
     where date = (selecione no máximo (data)
                     dos usuários u2
                    onde u1.userid = u2.userid)
Steve K
fonte
3
Provavelmente não é muito eficaz, Steve.
David Aldridge
7
Você provavelmente está subestimando o otimizador de consulta Oracle.
Rafał Dowgird 23/09/08
3
De modo nenhum. Isso certamente será implementado como uma verificação completa com uma junção de loop aninhada para obter as datas. Você está falando de io lógicos na ordem de quatro vezes o número de linhas na tabela e é péssimo para quantidades não triviais de dados.
David Aldridge
4
Para sua informação, "Não é eficiente, mas funciona" é o mesmo que "Funciona, mas não é eficiente". Quando desistimos da eficiência como objetivo de design?
David Aldridge
6
+1 porque, quando as tabelas de dados não têm milhões de linhas em comprimento, é a solução mais fácil de entender. quando você tem vários desenvolvedores de todos os níveis de habilidade modificando o código, a compreensibilidade é mais importante do que uma fração de segundo no desempenho que é imperceptível.
N00b
35

Não estando no trabalho, não tenho o Oracle em mãos, mas me lembro que o Oracle permite que várias colunas sejam correspondidas em uma cláusula IN, que deve pelo menos evitar as opções que usam uma subconsulta correlacionada, o que raramente é bom. idéia.

Algo assim, talvez (não lembro se a lista de colunas deve estar entre parênteses ou não):

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

EDIT: Apenas tentei de verdade:

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

Por isso, funciona, embora algumas das coisas novas mencionadas em outros lugares possam ter mais desempenho.

Mike Woodhouse
fonte
4
Isso funciona muito bem no PostgreSQL também. E eu gosto da simplicidade e da generalidade disso - a subconsulta diz "Aqui estão os meus critérios", a consulta externa diz "E aqui estão os detalhes que eu quero ver". +1.
Jrandom_hacker
13

Eu sei que você pediu pelo Oracle, mas no SQL 2005 agora usamos isso:


-- 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
mancaus
fonte
7

Não tenho o Oracle para testá-lo, mas a solução mais eficiente é usar consultas analíticas. Deve ser algo como isto:

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

Eu suspeito que você pode se livrar da consulta externa e colocar distintas no interior, mas não tenho certeza. Enquanto isso, eu sei que este funciona.

Se você quiser aprender sobre consultas analíticas, sugiro ler http://www.orafaq.com/node/55 e http://www.akadia.com/services/ora_analytic_functions.html . Aqui está o breve resumo.

Nas consultas analíticas, classifique todo o conjunto de dados e processe-o sequencialmente. Ao processá-lo, você particiona o conjunto de dados de acordo com certos critérios e, em seguida, para cada linha observa alguma janela (o padrão é o primeiro valor da partição para a linha atual - esse padrão também é o mais eficiente) e pode calcular valores usando um número de funções analíticas (cuja lista é muito semelhante às funções agregadas).

Nesse caso, aqui está o que a consulta interna faz. O conjunto de dados inteiro é classificado por UserId e Data DESC. Em seguida, processa-o de uma só vez. Para cada linha, você retorna o UserId e a primeira data vista para esse UserId (como as datas são classificadas em DESC, essa é a data máxima). Isso fornece sua resposta com linhas duplicadas. Em seguida, o DISTINCT externo esmaga duplicatas.

Este não é um exemplo particularmente espetacular de consultas analíticas. Para uma vitória muito maior, considere tomar uma tabela de recebimentos financeiros e calcular para cada usuário e recebedor, um total contínuo do que eles pagaram. As consultas analíticas resolvem isso com eficiência. Outras soluções são menos eficientes. É por isso que eles fazem parte do padrão SQL 2003. (Infelizmente, o Postgres ainda não os possui. Grrr ...)

user11318
fonte
Você também precisa retornar o valor da data para responder completamente à pergunta. Se isso significa outra cláusula first_value, sugiro que a solução seja mais complexa do que deveria ser, e o método analítico baseado em max (date) seja melhor.
David Aldridge
A declaração da pergunta não diz nada sobre o retorno da data. Você pode fazer isso adicionando outro PRIMEIRO (Data) ou apenas consultando a Data e alterando a consulta externa para um GROUP BY. Eu usaria o primeiro e esperaria que o otimizador calculasse os dois em uma única passagem.
User11318
"A pergunta não diz nada sobre retornar a data" ... sim, você está certo. Desculpa. Mas adicionar mais cláusulas FIRST_VALUE se tornaria uma bagunça muito rapidamente. É uma classificação de janela única, mas se você tiver 20 colunas para retornar para essa linha, terá escrito muito código para percorrer.
David Aldridge
Também me ocorre que essa solução não é determinística para dados em que um único ID de usuário possui várias linhas com a data máxima e valores diferentes. Mais uma falha na pergunta do que a resposta.
David Aldridge
1
Concordo que é dolorosamente detalhado. No entanto, esse não é geralmente o caso do SQL? E você está certo de que a solução é não determinística. Existem várias maneiras de lidar com laços e, às vezes, cada uma é o que você deseja.
User11318 23/09/08
6

Uma cláusula QUALIFY não seria mais simples e melhor?

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

Por contexto, no Teradata aqui, um teste de tamanho decente é executado nos anos 17 com esta versão QUALIFY e nos 23 com a solução 'inline view' / Aldridge # 1.

wcw
fonte
1
Esta é a melhor resposta na minha opinião. No entanto, tenha cuidado com a rank()função em situações em que há laços. Você pode acabar com mais de um rank=1. Melhor usar row_number()se você realmente deseja apenas um registro retornado.
Cartelechorhorse
1
Além disso, esteja ciente de que a QUALIFYcláusula é específica para o Teradata. No Oracle (pelo menos), é necessário aninhar sua consulta e filtrar usando uma WHEREcláusula na instrução de seleção de empacotamento (que provavelmente atinge um toque de desempenho, eu imagino).
Carthaftahorse
5

Em Oracle 12c+, você pode usar as consultas Top n junto com a função analítica rankpara conseguir isso de forma muito concisa, sem subconsultas:

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

O acima retorna todas as linhas com max my_date por usuário.

Se você deseja apenas uma linha com data máxima, substitua rankpor 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; 
Gurwinder Singh
fonte
5

Use ROW_NUMBER()para atribuir uma classificação exclusiva em descendente Datepara cada um e UserId, em seguida, filtre para a primeira linha de cada um UserId(por exemplo, 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;
markusk
fonte
5

Com o PostgreSQL 8.4 ou posterior, você pode usar isto:

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
Cito
fonte
3

Eu acho que você deve fazer essa variante para a consulta anterior:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)
stefano m
fonte
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  
Aheho
fonte
3

Só tive que escrever um exemplo "ao vivo" no trabalho :)

Este suporta vários valores para UserId na mesma data.

Colunas: ID do usuário, Valor, 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
)

Você pode usar FIRST_VALUE em vez de MAX e procurá-lo no plano de explicação. Não tive tempo de brincar com isso.

Obviamente, se você pesquisar em tabelas enormes, provavelmente será melhor usar dicas COMPLETAS na sua consulta.

Truper
fonte
3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
desagradável
fonte
2

Eu acho algo assim. (Perdoe-me por qualquer erro de sintaxe; estou acostumado a usar o HQL neste momento!)

EDIT: Também interpretou mal a pergunta! Corrigida a consulta ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)
jdmichal
fonte
Não atende à condição "for each UserId"
David Aldridge
Onde isso iria falhar? Para cada UserID em Users, será garantido que pelo menos uma linha contendo esse UserID seja retornada. Ou estou perdendo um caso especial em algum lugar?
jdmichal
2

(T-SQL) Primeiro, obtenha todos os usuários e seus maxdate. Associe-se à tabela para encontrar os valores correspondentes para os usuários nas datas máximas.

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

resultados:

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

A resposta aqui é apenas Oracle. Aqui está uma resposta um pouco mais sofisticada em todo o SQL:

Quem tem o melhor resultado geral da lição de casa (soma máxima de pontos de lição de casa)?

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 um exemplo mais difícil, que precisa de explicações, para o qual não tenho tempo atm:

Forneça o livro (ISBN e título) mais popular em 2008, ou seja, emprestado com mais frequência em 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);

Espero que isso ajude (alguém) .. :)

Atenciosamente, Guus

Guus
fonte
A resposta aceita é não "única Oracle" - é SQL padrão (apoiado por muitos DBMS)
a_horse_with_no_name
2

Supondo que a data seja exclusiva para um determinado ID do usuário, aqui estão alguns 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 
marc
fonte
2

Estou muito atrasado para a festa, mas o seguinte hack superará as subconsultas correlacionadas e qualquer função de análise, mas tem uma restrição: os valores devem ser convertidos em strings. Por isso, funciona para datas, números e outras strings. O código não parece bom, mas o perfil de execução é ótimo.

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

A razão pela qual esse código funciona tão bem é que ele só precisa varrer a tabela uma vez. Ele não requer índices e, o mais importante, não precisa classificar a tabela, o que a maioria das funções de análise exige. Os índices ajudarão, se você precisar filtrar o resultado para um único ID do usuário.

aLevelOfIndirection
fonte
É um bom plano de execução comparado à maioria, mas a aplicação de todos esses truques a mais de alguns campos seria tediosa e pode funcionar contra ele. Mas muito interessante - obrigado. veja sqlfiddle.com/#!4/2749b5/23
Used_By_Already
Você está certo de que pode se tornar entediante, e é por isso que isso deve ser feito apenas quando o desempenho da consulta exigir. Geralmente é o caso dos scripts ETL.
aLevelOfIndirection
isso é muito legal. fez algo semelhante usando LISTAGG, mas parece feio. O postgres possui uma alternativa melhor usando array_agg. veja a minha resposta :)
de 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 isso funciona. HTH

Zsolt Botykai
fonte
1

Eu acho que isso deve funcionar?

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
GateKiller
fonte
1

Na primeira tentativa, eu li mal a pergunta, seguindo a resposta principal, eis um exemplo completo com resultados corretos:

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)
KyleLanser
fonte
1

Isso também cuidará das duplicatas (retorne uma linha para cada 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
na43251
fonte
1

Acabei de testar isso e parece funcionar em uma tabela de registro

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

Isso deve ser tão simples quanto:

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

Solução para MySQL que não possui conceitos de partição 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

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

Ben Lin
fonte
Isso não funciona " em outros bancos de dados também ". Isso funciona apenas no MySQL e possivelmente no SQL Server porque possui um conceito semelhante de variáveis. Definitivamente não funcionará no Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Além disso, a resposta aceita é padrão ANSI SQL (que por conhecer apenas o MySQL não suporta)
a_horse_with_no_name
cavalo, acho que você está certo. Não tenho conhecimento sobre outros bancos de dados ou ANSI. Minha solução é capaz de resolver o problema no MySQL, que não possui suporte adequado para ANSI SQL para resolvê-lo da maneira padrão.
Ben Lin
1

Se você estiver usando o Postgres, poderá usar array_aggcomo

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

Não estou familiarizado com o Oracle. Isto é o que eu vim com

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 

Ambas as consultas retornam os mesmos resultados que a resposta aceita. Consulte SQLFiddles:

  1. Resposta aceita
  2. Minha solução com o Postgres
  3. Minha solução com Oracle
Bruno Calza
fonte
0

Se (UserID, Data) for único, ou seja, nenhuma data aparecer duas vezes para o mesmo usuário, então:

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;
finnw
fonte
Eu acredito que você precisa para se juntar pela UserID bem
Tom H
0
select   UserId,max(Date) over (partition by UserId) value from users;
Amitābha
fonte
2
Isso retornará todas as linhas, não apenas uma linha por usuário.
quer