Obtener la fila que tiene el valor máximo para una columna

576

Mesa:

UserId, Value, Date.

Quiero obtener el UserId, Valor para el máximo (Fecha) para cada UserId. Es decir, el Valor para cada UserId que tiene la última fecha. ¿Hay alguna manera de hacer esto simplemente en SQL? (Preferiblemente Oracle)

Actualización: disculpas por cualquier ambigüedad: necesito obtener TODOS los ID de usuario. Pero para cada UserId, solo esa fila donde ese usuario tiene la última fecha.

Umang
fuente
21
¿Qué sucede si hay varias filas que tienen el valor de fecha máximo para un ID de usuario en particular?
David Aldridge
¿Cuáles son los campos clave de la tabla?
vamosrafa
algunas soluciones a continuación comparadas: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already
1
@DavidAldridge, esa columna es probablemente única.
Pacerier

Respuestas:

398

Esto recuperará todas las filas para las cuales el valor de la columna my_date es igual al valor máximo de my_date para ese ID de usuario. Esto puede recuperar varias filas para el ID de usuario donde la fecha máxima está en varias filas.

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

"Funciones analíticas rock"

Editar: Con respecto al primer comentario ...

"el uso de consultas analíticas y una autounión derrota el propósito de las consultas analíticas"

No hay autounión en este código. En cambio, hay un predicado colocado en el resultado de la vista en línea que contiene la función analítica, un asunto muy diferente y una práctica completamente estándar.

"La ventana predeterminada en Oracle es desde la primera fila de la partición hasta la actual"

La cláusula de ventanas solo es aplicable en presencia de la orden por cláusula. Sin orden por cláusula, no se aplica ninguna cláusula de ventana por defecto y ninguna se puede especificar explícitamente.

El código funciona

David Aldridge
fuente
39
Cuando se aplica a una tabla que tiene 8.8 millones de filas, esta consulta tomó la mitad del tiempo de las consultas en algunas otras respuestas altamente votadas.
Derek Mahar
44
¿A alguien le importa publicar un enlace al equivalente de MySQL de este, si hay alguno?
olor a
2
¿No podría esto devolver duplicados? P.ej. si dos filas tienen el mismo user_id y la misma fecha (que es el máximo).
jastr
2
@jastr Creo que eso fue reconocido en la pregunta
David Aldridge
3
En lugar de MAX(...) OVER (...)usted, también puede usar ROW_NUMBER() OVER (...)(para el top-n-per-group) o RANK() OVER (...)(para el mejor-n-per-group).
MT0
441

Veo que muchas personas usan subconsultas o funciones específicas del proveedor para hacer esto, pero a menudo hago este tipo de consulta sin subconsultas de la siguiente manera. Utiliza SQL simple y estándar, por lo que debería funcionar en cualquier 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;

En otras palabras: busque la fila desde t1donde no existe otra fila con la misma UserIdy mayor fecha.

(Puse el identificador "Fecha" en delimitadores porque es una palabra reservada de SQL).

En caso de que t1."Date" = t2."Date"aparezca, se duplicará. Por lo general, las tablas tienen auto_inc(seq)clave, por ejemplo id. Para evitar duplicar se puede utilizar de la siguiente manera:

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 comentar de @Farhan:

Aquí hay una explicación más detallada:

Una combinación externa intentos para unirse t1con t2. Por defecto, t1se devuelven todos los resultados de , y si hay una coincidencia t2, también se devuelve. Si no hay coincidencia t2para una fila determinada de t1, la consulta aún devuelve la fila de t1y se utiliza NULLcomo marcador de posición para todas t2las columnas de. Así es como funcionan las uniones externas en general.

El truco en esta consulta es diseñar la condición de coincidencia de la unión de modo que t2deba coincidir con la misma userid , y una mayor date . La idea es que si existe una fila t2que tiene una mayor date, entonces la fila en la t1que se compara no puede ser la mejor datepara eso userid. Pero si no hay coincidencia, es decir, si no existe una fila t2con un valor mayor dateque la fila de entrada t1, sabemos que la fila de adentro t1fue la fila con el mayor valor datepara la dada userid.

En esos casos (cuando no hay coincidencia), las columnas de t2serán NULL, incluso las columnas especificadas en la condición de unión. Por eso lo usamos WHERE t2.UserId IS NULL, porque estamos buscando los casos en los que no se encontró una fila con una mayor datepara lo dado userid.

Bill Karwin
fuente
77
Wow Bill Esta es la solución más creativa para este problema que he visto. También es bastante eficiente en mi conjunto de datos bastante grande. Esto seguramente supera muchas de las otras soluciones que he visto o mis propios intentos de resolver este dilema.
Justin Noel el
37
Cuando se aplica a una tabla que tiene 8.8 millones de filas, esta consulta tomó casi el doble que la respuesta aceptada.
Derek Mahar
16
@Derek: las optimizaciones dependen de la marca y la versión de RDBMS, así como de la presencia de índices, tipos de datos, etc. apropiados
Bill Karwin,
77
En MySQL, este tipo de consulta parece hacer que se repita el resultado de una unión cartesiana entre las tablas, lo que resulta en un tiempo O (n ^ 2). El uso del método de subconsulta redujo el tiempo de consulta de 2.0s a 0.003s. YMMV.
Jesse
1
¿Hay alguna manera de adaptar esto para que coincida con las filas donde la fecha es la fecha más grande menor o igual a la fecha dada por un usuario? Por ejemplo, si el usuario da la fecha "23-OCT-2011", y la tabla incluye filas para "24-OCT-2011", "22-OCT-2011", "20-OCT-2011", entonces quiero obtener "22-OCT-2011". He estado rascándome la cabeza y leyendo este fragmento por un tiempo ahora ...
Cory Kendall
164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
Dave Costa
fuente
3
En mis pruebas usando una tabla que tiene un gran número de filas, esta solución tomó aproximadamente el doble de tiempo que la respuesta aceptada.
Derek Mahar
77
Muestre su prueba, por favor
Rob van Wijk
Confirmo que es mucho más rápido que otras soluciones
tamersalama
55
problema es que no devuelve el registro completo
Used_By_Already
@ user2067753 No, no devuelve el registro completo. Puede usar la misma expresión MAX () .. KEEP .. en varias columnas, por lo que puede seleccionar todas las columnas que necesita. Pero es inconveniente si desea una gran cantidad de columnas y prefiere usar SELECT *.
Dave Costa
51

No sé los nombres exactos de sus columnas, pero sería algo como esto:

    seleccione userid, value
      de los usuarios u1
     donde fecha = (seleccione max (fecha)
                     de los usuarios u2
                    donde u1.userid = u2.userid)
Steve K
fuente
3
Probablemente no muy eficiente, Steve.
David Aldridge
77
Probablemente esté subestimando el optimizador de consultas de Oracle.
Rafał Dowgird el
3
De ningún modo. Esto seguramente se implementará como un escaneo completo con una unión de bucle anidado para obtener las fechas. Estás hablando de io lógicos en el orden de 4 veces el número de filas en la tabla y temes por cantidades de datos no triviales.
David Aldridge
44
Para su información, "No es eficiente, pero funciona" es lo mismo que "Funciona, pero no es eficiente". ¿Cuándo renunciamos a la eficiencia como objetivo de diseño?
David Aldridge
66
+1 porque cuando sus tablas de datos no tienen millones de filas de largo en todos los casos, esta es la solución más fácil de entender. cuando tienes múltiples desarrolladores de todos los niveles de habilidad modificando el código, la comprensión es más importante que una fracción de segundo en rendimiento que es imperceptible.
n00b
35

No estoy en el trabajo, no tengo Oracle a mano, pero parece recordar que Oracle permite que varias columnas coincidan en una cláusula IN, que al menos debería evitar las opciones que usan una subconsulta correlacionada, que rara vez es una buena idea.

Algo como esto, tal vez (no recuerdo si la lista de columnas debe estar entre paréntesis o no):

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

EDITAR: Solo lo probé de verdad:

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 lo tanto, funciona, aunque algunas de las cosas novedosas mencionadas en otros lugares pueden ser más efectivas.

Mike Woodhouse
fuente
44
Esto también funciona bien en PostgreSQL. Y me gusta su simplicidad y generalidad: la subconsulta dice "Aquí está mi criterio", la consulta externa dice "Y aquí están los detalles que quiero ver". +1.
j_random_hacker
13

Sé que solicitó Oracle, pero en SQL 2005 ahora usamos esto:


-- 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
fuente
7

No tengo Oracle para probarlo, pero la solución más eficiente es usar consultas analíticas. Debería verse más o menos así:

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

Sospecho que puede deshacerse de la consulta externa y poner distinta en la interna, pero no estoy seguro. Mientras tanto, sé que este funciona.

Si desea obtener información sobre consultas analíticas, le sugiero que lea http://www.orafaq.com/node/55 y http://www.akadia.com/services/ora_analytic_functions.html . Aquí está el breve resumen.

Bajo el capó, las consultas analíticas clasifican todo el conjunto de datos y luego lo procesan secuencialmente. A medida que lo procesa, divide el conjunto de datos de acuerdo con ciertos criterios, y luego, para cada fila, mira alguna ventana (el valor predeterminado es el primer valor de la partición en la fila actual, ese valor predeterminado también es el más eficiente) y puede calcular valores usando un cantidad de funciones analíticas (la lista de las cuales es muy similar a las funciones agregadas).

En este caso, esto es lo que hace la consulta interna. Todo el conjunto de datos se ordena por UserId y luego Date DESC. Luego lo procesa en una sola pasada. Para cada fila devuelve el UserId y la primera Fecha vista para ese UserId (dado que las fechas se ordenan DESC, esa es la fecha máxima). Esto le da su respuesta con filas duplicadas. Luego, el DISTINCT externo aplasta los duplicados.

Este no es un ejemplo particularmente espectacular de consultas analíticas. Para obtener una ganancia mucho mayor, considere tomar una tabla de recibos financieros y calcular para cada usuario y recibo, un total acumulado de lo que pagaron. Las consultas analíticas lo resuelven de manera eficiente. Otras soluciones son menos eficientes. Es por eso que son parte del estándar SQL 2003. (Desafortunadamente Postgres aún no los tiene. Grrr ...)

usuario11318
fuente
También debe devolver el valor de la fecha para responder la pregunta por completo. Si eso significa otra cláusula first_value, sugeriría que la solución es más compleja de lo que debería ser, y el método analítico basado en max (date) se lee mejor.
David Aldridge
La declaración de la pregunta no dice nada acerca de devolver la fecha. Puede hacerlo agregando otro FIRST (Fecha) o simplemente consultando la Fecha y cambiando la consulta externa a GROUP BY. Usaría el primero y esperaría que el optimizador calcule ambos en una sola pasada.
user11318
"La pregunta no dice nada acerca de devolver la fecha" ... sí, tienes razón. Lo siento. Pero agregar más cláusulas FIRST_VALUE se volvería complicado bastante rápido. Es un tipo de ventana única, pero si tenía 20 columnas para devolver para esa fila, entonces ha escrito mucho código para pasar.
David Aldridge
También se me ocurre que esta solución no es determinista para los datos en los que un único ID de usuario tiene varias filas que tienen la fecha máxima y diferentes VALORES. Sin embargo, es más un error en la pregunta que en la respuesta.
David Aldridge
1
Estoy de acuerdo en que es dolorosamente detallado. Sin embargo, ¿no es ese generalmente el caso con SQL? Y tiene razón en que la solución no es determinista. Hay varias formas de lidiar con los lazos, y a veces cada una es lo que quieres.
user11318
6

¿No sería una cláusula QUALIFY más simple y mejor?

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

Por contexto, en Teradata aquí, una prueba de tamaño decente de esto se ejecuta en 17 segundos con esta versión QUALIFY y en 23 segundos con la 'vista en línea' / solución Aldridge # 1.

wcw
fuente
1
Esta es la mejor respuesta en mi opinión. Sin embargo, tenga cuidado con la rank()función en situaciones donde hay vínculos. Podrías terminar con más de uno rank=1. Es mejor usarlo row_number()si realmente desea que se devuelva un solo registro.
cartbeforehorse
1
Además, tenga en cuenta que la QUALIFYcláusula es específica de Teradata. En Oracle (al menos) debe anidar su consulta y filtrar usando una WHEREcláusula en la declaración de selección de ajuste (que probablemente suponga un toque de rendimiento, me imagino).
cartbeforehorse
6

Con PostgreSQL 8.4 o posterior, puede usar esto:

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
fuente
5

En Oracle 12c+, puede usar las consultas Top n junto con la función analítica rankpara lograr esto de manera concisa sin subconsultas:

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

Lo anterior devuelve todas las filas con max my_date por usuario.

Si solo desea una fila con la fecha máxima, reemplace la 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; 
Gurwinder Singh
fuente
5

Use ROW_NUMBER()para asignar una clasificación única al descender Datepara cada uno UserId, luego filtre a la primera fila para cada uno UserId(es decir, 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
fuente
3

Creo que debes hacer esta variante a la consulta anterior:

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

Solo tenía que escribir un ejemplo "en vivo" en el trabajo :)

Éste admite múltiples valores para UserId en la misma fecha.

Columnas: ID de usuario, valor, fecha

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
)

Puede usar FIRST_VALUE en lugar de MAX y buscarlo en el plan de explicación. No tuve tiempo de jugar con eso.

Por supuesto, si busca en tablas enormes, probablemente sea mejor si utiliza sugerencias COMPLETAS en su consulta.

Truper
fuente
3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
nouky
fuente
2

Creo que algo como esto (Perdóname por cualquier error de sintaxis; ¡estoy acostumbrado a usar HQL en este momento!)

EDITAR: ¡También leí mal la pregunta! Corregida la consulta ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)
jdmichal
fuente
No cumple con la condición "para cada Id. De usuario"
David Aldridge el
¿Dónde fallaría? Por cada ID de usuario en Usuarios, se garantizará que se devolverá al menos una fila que contenga ese ID de usuario. ¿O me estoy perdiendo un caso especial en alguna parte?
jdmichal
2

(T-SQL) Primero obtenga todos los usuarios y su maxdate. Únase a la tabla para encontrar los valores correspondientes para los usuarios en las fechas 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
fuente
2

La respuesta aquí es solo Oracle. Aquí hay una respuesta un poco más sofisticada en todos los SQL:

¿Quién tiene el mejor resultado general de tarea (suma máxima de puntos de tarea)?

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)

Y un ejemplo más difícil, que necesita alguna explicación, para el que no tengo cajero automático:

Entregue el libro (ISBN y título) que es más popular en 2008, es decir, que se toma prestado con mayor frecuencia en 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 esto ayude (a cualquiera) .. :)

Saludos, Guus

Guus
fuente
La respuesta aceptada no es "solo Oracle" - es SQL estándar (compatible con muchos DBMS)
a_horse_with_no_name
2

Suponiendo que la fecha es única para un ID de usuario dado, aquí hay algunos 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 
bagazo
fuente
2

Llego bastante tarde a la fiesta, pero el siguiente truco superará tanto las subconsultas correlacionadas como cualquier función de análisis, pero tiene una restricción: los valores deben convertirse en cadenas. Entonces funciona para fechas, números y otras cadenas. El código no se ve bien, pero el perfil de ejecución es excelente.

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

La razón por la que este código funciona tan bien es que solo necesita escanear la tabla una vez. No requiere ningún índice y, lo que es más importante, no necesita ordenar la tabla, como sí lo hacen la mayoría de las funciones de análisis. Sin embargo, los índices ayudarán si necesita filtrar el resultado para un único ID de usuario.

aLevelOfIndirection
fuente
Es un buen plan de ejecución en comparación con la mayoría, pero aplicar todos esos trucos a más de unos pocos campos sería tedioso y podría funcionar en su contra. Pero muy interesante, gracias. ver sqlfiddle.com/#!4/2749b5/23
Used_By_Already
Tiene razón, puede volverse tedioso, por lo que esto debe hacerse solo cuando el rendimiento de la consulta lo requiera. Tal es a menudo el caso con los scripts ETL.
aLevelOfIndirection
esto esta muy bien. hizo algo similar usando LISTAGG pero se ve feo. postgres tiene una mejor altenativa usando array_agg. mira mi respuesta :)
Bruno Calza
2

Si estás usando Postgres, puedes usar array_agglike

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

No estoy familiarizado con Oracle. Esto es lo que se me ocurrió

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 consultas devuelven los mismos resultados que la respuesta aceptada. Ver SQLFiddles:

  1. Respuesta aceptada
  2. Mi solución con Postgres
  3. Mi solución con Oracle
Bruno Calza
fuente
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

En mi humilde opinión esto funciona. HTH

Zsolt Botykai
fuente
1

¿Creo que esto debería 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
fuente
1

Primero intente leer mal la pregunta, siguiendo la respuesta principal, aquí hay un ejemplo completo con resultados correctos:

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
fuente
1

Esto también se encargará de los duplicados (devolver una fila por 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
fuente
1

Acabo de probar esto y parece funcionar en una tabla de registro

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

Esto debería ser tan simple como:

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

Solución para MySQL que no tiene conceptos de partición 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

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

Ben Lin
fuente
Esto no funciona " en otros DB también ". Esto solo funciona en MySQL y posiblemente en SQL Server porque tiene un concepto similar de variables. Definitivamente no funcionará en Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Además, la respuesta aceptada es estándar ANSI SQL (que sólo conocen por MySQL no soporta)
a_horse_with_no_name
caballo, supongo que tienes razón. No tengo conocimiento sobre otras bases de datos o ANSI. Mi solución es capaz de resolver el problema en MySQL, que no tiene soporte adecuado para ANSI SQL para resolverlo de manera estándar.
Ben Lin
0

Si (UserID, Date) es único, es decir, no aparece una fecha dos veces para el mismo usuario, entonces:

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
fuente
Creo que también debes unirte con el ID de usuario
Tom H
0
select   UserId,max(Date) over (partition by UserId) value from users;
Amitābha
fuente
2
Esto devolverá todas las filas, no solo una por usuario.
Jon Heller