transponer sumar multiple filas convertir columns columnas 10g sql oracle greatest-n-per-group

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



sql transponer columnas a filas (30)

Mesa:

UserId, Value, Date.

Quiero obtener el Id. De usuario, Valor para el máximo (Fecha) para cada Id. De usuario. Es decir, el valor para cada UserId que tiene la fecha más reciente. ¿Hay una manera de hacer esto simplemente en SQL? (Preferiblemente Oracle)

Actualización: disculpas por cualquier ambigüedad: necesito obtener TODOS los UserIds. Pero para cada UserId, solo esa fila donde ese usuario tiene la fecha más reciente.


¿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


¿Una cláusula de CALIFICACIÓN no sería tan simple como mejor?

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

Para el contexto, en Teradata aquí, una prueba de tamaño decente se ejecuta en 17s con esta versión de CALIFICACIÓN y en 23s con la solución # 1 / Aldridge de ''vista en línea''.


(T-SQL) Primero obtenga todos los usuarios y su fecha máxima. Ú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


Al no estar trabajando, no tengo Oracle a mano, pero me 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, lo que rara vez es una buena opción. idea.

Algo como esto, tal vez (no puedo recordar 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)

EDIT: acaba de intentarlo 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

Así que funciona, aunque algunas de las cosas nuevas y famosas que se mencionan en otros lugares pueden ser más eficaces.


Con PostgreSQL 8.4 o posterior, puedes 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


Creo que deberías hacer esta variante a la consulta anterior:

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


En Oracle 12c+ , puede usar las consultas Top n junto con el rank función analítica para lograr esto de manera muy 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 el rank con el número de 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;


Esto debería ser tan simple como:

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


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 del rock"

Edit: Con respecto al primer comentario ...

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

No hay auto-unión en este código. En cambio, hay un predicado colocado sobre 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 en la partición hasta la actual"

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

El código funciona.


Esto también se ocupará de los duplicados (devuelva una fila para cada ID de usuario):

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


La respuesta aquí es solo Oracle. Aquí hay una respuesta un poco más sofisticada en todo 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 una explicación, para el que no tengo tiempo de cajero automático:

Registre 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


Llego tarde a la fiesta, pero el siguiente truco superará tanto a las subconsultas correlacionadas como a cualquier función de análisis, pero tiene una restricción: los valores deben convertirse en cadenas. Así funciona para fechas, números y otras cadenas. El código no parece bueno 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 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.


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

select userid, value from users u1 where date = (select max(date) from users u2 where u1.userid = u2.userid)


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

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 diferenciarla de la interna, pero no estoy seguro. Mientras tanto, sé que éste funciona.

Si desea obtener información sobre consultas analíticas, sugiero leer 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, particiona el conjunto de datos de acuerdo con ciertos criterios, y luego para cada fila mira alguna ventana (por defecto, el primer valor en la partición de la fila actual, que también es el más eficiente) y puede calcular los valores usando Número de funciones analíticas (cuya lista es muy similar a las funciones agregadas).

En este caso aquí es lo que hace la consulta interna. Todo el conjunto de datos está ordenado por UserId y luego por Fecha DESC. Luego lo procesa en una sola pasada. Para cada fila, devuelve el Id. De usuario y la primera Fecha de visualización de ese Id. De usuario (ya que las fechas están ordenadas por DESC, esa es la fecha máxima). Esto le da su respuesta con filas duplicadas. Entonces los aplastamientos DISTINCT externos se duplican.

Este no es un ejemplo particularmente espectacular de consultas analíticas. Para 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 no los tiene todavía. Grrr ...)


Pienso algo como esto. (Perdóneme por cualquier error de sintaxis; ¡estoy acostumbrado a usar HQL en este momento!)

EDITAR: También leer mal la pregunta! Se corrigió la consulta ...

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


Primero intento, malinterpreté 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)


Sé que pediste 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


Si (UserID, Date) es único, es decir, si no aparece la 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;


Si estás usando Postgres, puedes usar array_agg como

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

Solo probé esto y parece funcionar en una tabla de registro

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


Solo tuve que escribir un ejemplo "en vivo" en el trabajo :)

Éste admite varios 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 grandes, probablemente sea mejor si usa sugerencias COMPLETAS en su consulta.



Suponiendo que la fecha es única para un UserID 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


Use ROW_NUMBER() para asignar una clasificación única en la Date descendente para cada UserId , luego filtre a la primera fila para cada 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;


Veo que muchas personas usan subconsultas o, si no, características 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 de t1 donde no existe ninguna otra fila con el mismo UserId y una Fecha mayor.

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

En caso de que t1."Date" = t2."Date" , aparece la duplicación. Por lo general, las tablas tienen la auto_inc(seq) , por ejemplo, id . Para evitar la duplicación se puede utilizar a continuación:

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

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

Una combinación externa intenta unir t1 con t2 . De forma predeterminada, se devuelven todos los resultados de t1 y, si hay una coincidencia en t2 , también se devuelve. Si no hay coincidencia en t2 para una fila dada de t1 , la consulta aún retorna la fila de t1 , y usa NULL como un marcador de posición para todas las columnas de t2 . 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 el mismo userid y una date mayor . La idea es que si existe una fila en t2 que tenga una date mayor, entonces la fila en t1 que se compara no puede ser la mejor date para ese userid . Pero si no hay coincidencia, es decir, si no existe una fila en t2 con una date mayor que la fila en t1 , sabemos que la fila en t1 fue la fila con la date más grande para el userid dado.

En esos casos (cuando no hay coincidencia), las columnas de t2 serán NULL , incluso las columnas especificadas en la condición de unión. Por eso utilizamos WHERE t2.UserId IS NULL , porque buscamos los casos en los que no se encontró una fila con una date mayor para el userid dado.


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


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


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


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


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