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.
sql
oracle
greatest-n-per-group
Umang
fuente
fuente
Respuestas:
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.
"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
fuente
MAX(...) OVER (...)
usted, también puede usarROW_NUMBER() OVER (...)
(para el top-n-per-group) oRANK() OVER (...)
(para el mejor-n-per-group).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.
En otras palabras: busque la fila desde
t1
donde no existe otra fila con la mismaUserId
y 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 tienenauto_inc(seq)
clave, por ejemploid
. Para evitar duplicar se puede utilizar de la siguiente manera:Re comentar de @Farhan:
Aquí hay una explicación más detallada:
Una combinación externa intentos para unirse
t1
cont2
. Por defecto,t1
se devuelven todos los resultados de , y si hay una coincidenciat2
, también se devuelve. Si no hay coincidenciat2
para una fila determinada det1
, la consulta aún devuelve la fila det1
y se utilizaNULL
como marcador de posición para todast2
las 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
t2
deba coincidir con la mismauserid
, y una mayordate
. La idea es que si existe una filat2
que tiene una mayordate
, entonces la fila en lat1
que se compara no puede ser la mejordate
para esouserid
. Pero si no hay coincidencia, es decir, si no existe una filat2
con un valor mayordate
que la fila de entradat1
, sabemos que la fila de adentrot1
fue la fila con el mayor valordate
para la dadauserid
.En esos casos (cuando no hay coincidencia), las columnas de
t2
seránNULL
, incluso las columnas especificadas en la condición de unión. Por eso lo usamosWHERE t2.UserId IS NULL
, porque estamos buscando los casos en los que no se encontró una fila con una mayordate
para lo dadouserid
.fuente
fuente
No sé los nombres exactos de sus columnas, pero sería algo como esto:
fuente
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):
EDITAR: Solo lo probé de verdad:
Por lo tanto, funciona, aunque algunas de las cosas novedosas mencionadas en otros lugares pueden ser más efectivas.
fuente
Sé que solicitó Oracle, pero en SQL 2005 ahora usamos esto:
fuente
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í:
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 ...)
fuente
¿No sería una cláusula QUALIFY más simple y mejor?
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.
fuente
rank()
función en situaciones donde hay vínculos. Podrías terminar con más de unorank=1
. Es mejor usarlorow_number()
si realmente desea que se devuelva un solo registro.QUALIFY
cláusula es específica de Teradata. En Oracle (al menos) debe anidar su consulta y filtrar usando unaWHERE
cláusula en la declaración de selección de ajuste (que probablemente suponga un toque de rendimiento, me imagino).Con PostgreSQL 8.4 o posterior, puede usar esto:
fuente
En
Oracle 12c+
, puede usar las consultas Top n junto con la función analíticarank
para lograr esto de manera concisa sin subconsultas:Lo anterior devuelve todas las filas con max my_date por usuario.
Si solo desea una fila con la fecha máxima, reemplace la
rank
conrow_number
:fuente
Use
ROW_NUMBER()
para asignar una clasificación única al descenderDate
para cada unoUserId
, luego filtre a la primera fila para cada unoUserId
(es decir,ROW_NUMBER
= 1).fuente
Creo que debes hacer esta variante a la consulta anterior:
fuente
fuente
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
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.
fuente
fuente
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 ...
fuente
(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.
resultados:
fuente
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)?
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.
Espero que esto ayude (a cualquiera) .. :)
Saludos, Guus
fuente
Suponiendo que la fecha es única para un ID de usuario dado, aquí hay algunos TSQL:
fuente
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.
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.
fuente
Si estás usando Postgres, puedes usar
array_agg
likeNo estoy familiarizado con Oracle. Esto es lo que se me ocurrió
Ambas consultas devuelven los mismos resultados que la respuesta aceptada. Ver SQLFiddles:
fuente
En mi humilde opinión esto funciona. HTH
fuente
¿Creo que esto debería funcionar?
fuente
Primero intente leer mal la pregunta, siguiendo la respuesta principal, aquí hay un ejemplo completo con resultados correctos:
-
-
fuente
Esto también se encargará de los duplicados (devolver una fila por cada user_id):
fuente
Acabo de probar esto y parece funcionar en una tabla de registro
fuente
Esto debería ser tan simple como:
fuente
Solución para MySQL que no tiene conceptos de partición KEEP, DENSE_RANK.
Referencia: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
fuente
Si (UserID, Date) es único, es decir, no aparece una fecha dos veces para el mismo usuario, entonces:
fuente
fuente