获取具有列的最大值的行

表:

UserId, Value, Date. 

我想获得每个UserId的最大(date)UserId,价值。 也就是说,每个UserId具有最新date的值。 有没有办法简单地在SQL中做到这一点? (最好是甲骨文)

更新:道歉的任何歧义:我需要得到所有的用户ID。 但是对于每个UserId,只有那个用户有最新date的行。

这将检索my_date列值等于该用户标识的my_date的最大值的所有行。 这可能会检索最多date在多行上的userid多行。

 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 

“分析函数摇滚”

编辑:关于第一个评论…

“使用分析查询和自我join会破坏分析查询的目的”

此代码中没有自我join。 而是有一个谓词放在包含分析函数的内联视图的结果上 – 这是一个非常不同的问题,也是完全标准的实践。

“Oracle中的默认窗口是从分区中的第一行到当前的”

窗口条款只适用于存在order by子句的情况。 没有order by子句的情况下,默认不应用windowing子句,也不能明确指定任何子句。

代码工作。

我发现很多人使用子查询或其他特定于供应商的function来做到这一点,但是我经常以下面的方式做这种没有子查询的查询。 它使用普通的标准SQL,因此它可以在任何品牌的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; 

换句话说:从t1中获取行,其中不存在具有相同UserId和更大Date的其他行。

(我把标识符“Date”放在分隔符中,因为它是一个SQL保留字。)

如果t1."Date" = t2."Date" ,则出现翻倍。 通常表具有auto_inc(seq)键,例如id 。 避免加倍可以使用如下:

 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; 

来自@Farhan的评论:

这里有一个更详细的解释:

外连接尝试将t1与t2连接。 默认情况下,返回t1的所有结果, 如果 t2中有匹配,则返回。 如果t1的给定行在t2中没有匹配,则查询仍然返回t1的行,并且使用NULL作为所有t2列的占位符。 一般来说,这就是外连接的工作方式。

这个查询中的技巧是devise连接的匹配条件,使得t2必须匹配相同的用户标识和更大的date。 这个想法是,如果一个行在t2中有一个更大的date,那么它与t1进行比较的行不能是该userid的最大date。 但是,如果没有匹配 – 即如果在t2中没有行存在比t1中的行更大的date – 我们知道t1中的行是给定用户标识的date最长的行。

在这些情况下(当没有匹配时),t2的列将是NULL – 甚至连接条件中指定的列。 所以这就是为什么我们使用WHERE t2.UserId IS NULL ,因为我们正在寻找没有find具有给定用户标识的更长date的行的情况。

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

我不知道你的确切的列名称,但它会是这样的:

    select用户ID,值
      来自用户u1
      where date =(select max(date)
                     来自用户u2
                    其中u1.userid = u2.userid)

没有工作,我没有Oracle,但我似乎记得,Oracle允许在IN子句中匹配多个列,这应该至less避免使用相关子查询的选项,这很less是一个好的理念。

这样的东西,也许(不记得列列表是否应该被括起来):

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

编辑:只是尝试了它的真实:

 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 

所以,虽然其他地方提到的一些新的东西可能会更高性能,但它的工作原理。

我知道你问Oracle,但在SQL 2005中我们现在使用这个:

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

QUALIFY条款不是最简单最好的吗?

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

对于Teradata来说,Teradata在这个testing中运行了17分钟的testing版本,而在23分钟的时候testing了“内联视图”/ Aldridge解决scheme#1。

我没有Oracle来testing它,但最有效的解决scheme是使用分析查询。 它应该看起来像这样:

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

我怀疑你可以摆脱外部的查询,并把内部明显,但我不知道。 在此期间,我知道这个作品。

如果你想了解分析查询,build议阅读http://www.orafaq.com/node/55和;http://www.akadia.com/services/ora_analytic_functions.html 。 这里是简短的总结。

在引擎下分析查询对整个数据集进行sorting,然后按顺序进行处理。 当你处理它时,你根据一定的标准对数据集进行分区,然后每行查看一些窗口(默认是分区中的第一个值到当前行 – 这个默认值也是最有效的),并且可以使用分析函数的数量(列表与聚合函数非常相似)。

在这种情况下,这是内部查询所做的。 整个数据集按UserId和Date DESCsorting。 然后它一次处理它。 对于每一行你返回UserId和第一个看到该UserId的date(因为datesortingDESC,这是最大date)。 这给你你的答案重复行。 然后外面的DISTINCT压缩重复。

这不是分析查询的一个特别壮观的例子。 对于一个更大的胜利考虑拿一个财务收据表和计算每个用户和收据,他们支付了什么运行总额。 分析查询有效地解决了这个问题 其他解决scheme效率较低。 这就是为什么他们是2003 SQL标准的一部分。 (不幸的是,Postgres还没有.Grrr …)

有了PostgreSQL 9,你可以使用这个:

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

只是不得不写一个“活”的例子在工作:)

这一个在一天支持UserId的多个值。

列:UserId,值,date

 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 ) 

您可以使用FIRST_VALUE而不是MAX,并在解释计划中查找它。 我没有时间玩。

当然,如果通过巨大的表进行search,如果在查询中使用FULL提示可能会更好。

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

我想这样的事情。 (原谅我的任何语法错误,我习惯在这一点上使用HQL!)

编辑:也误读了这个问题! 更正了查询…

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

我的东西你shuold使这个变种先前的查询:

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

(T-SQL)首先获取所有用户及其最大date。 join表格以查找最大date的用户的相应值。

 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 

结果:

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

这里的答案只是Oracle。 所有的SQL都有一个更复杂的答案:

谁有最好的整体功课成绩(功课分数最高)?

 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) 

还有一个更困难的例子,需要一些解释,我没有时间atm:

提供2008年最受欢迎的书籍(ISBN和标题),即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); 

希望这可以帮助(任何人).. 🙂

问候,古斯

假设Date对于一个给定的用户ID是唯一的,下面是一些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 

我已经很晚了,但下面的黑客将会胜过相关的子查询和任何分析函数,但有一个限制:值必须转换为string。 所以它适用于date,数字和其他string。 该代码看起来不太好,但执行configuration文件是伟大的。

 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 

这段代码工作得很好的原因是它只需要扫描一次表格。 它不需要任何索引,最重要的是它不需要对大多数分析函数所执行的表进行sorting。 索引将有所帮助,但如果您需要筛选单个用户标识的结果。

Oracle 12c+ ,您可以使用Top n查询以及分析函数rank来非常简洁地实现此function, 而无需使用子查询:

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

以上返回每个用户的最大my_date的所有行。

如果您只需要一个最大date的行,则用row_numberreplace该行:

 select * from your_table order by row_number() over (partition by user_id order by my_date desc) fetch first 1 row with ties; 
 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 

恕我直言,这工作。 HTH

我认为这应该工作?

 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 

首先尝试我误解了这个问题,在最上面的答案,这里是一个完整的例子与正确的结果:

 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) 

这也将处理重复(每个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 

只是testing了这一点,它似乎在日志表上工作

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

这应该像下面这样简单:

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

如果你使用的是Postgres,你可以使用array_agg

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

我不熟悉Oracle。 这是我想出来的

 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 

两个查询都返回与接受的答案相同的结果。 请参阅SQLFiddles:

  1. 接受的答案
  2. 我与Postgres的解决scheme
  3. 我与Oracle的解决scheme

检查这个链接,如果你的问题看起来类似于那个页面,那么我会build议你下面的查询将给出该链接的解决scheme

select distinct sno,item_name,max(start_date) over(partition by sno),max(end_date) over(partition by sno),max(creation_date) over(partition by sno), max(last_modified_date) over(partition by sno) from uniq_select_records order by sno,item_name asc;

将给出与该链接有关的准确结果

如果(用户ID,date)是唯一的,也就是说没有date出现两次同一用户,则:

 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; 
 select UserId,max(Date) over (partition by UserId) value from users; 

MySQL的解决scheme没有分区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 

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

Interesting Posts