表:
UserId, Value, Date.
我想获得UserId,为每个UserId的最大值(日期)的值。也就是说,具有最新日期的每个UserId的值。有没有一种方法可以在SQL中简单地做到这一点?(最好是Oracle)
更新:为任何歧义道歉:我需要得到所有的用户id。但是对于每个UserId,只有该用户拥有最新日期的行。
表:
UserId, Value, Date.
我想获得UserId,为每个UserId的最大值(日期)的值。也就是说,具有最新日期的每个UserId的值。有没有一种方法可以在SQL中简单地做到这一点?(最好是Oracle)
更新:为任何歧义道歉:我需要得到所有的用户id。但是对于每个UserId,只有该用户拥有最新日期的行。
当前回答
我知道你要求使用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
其他回答
我看到许多人使用子查询或其他窗口函数来执行此操作,但我经常以以下方式执行这种不带子查询的查询。它使用普通的标准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。“日期”,加倍出现。通常表有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中有匹配,也将返回。如果t2中t1的给定行没有匹配,那么查询仍然返回t1的行,并对t2的所有列使用NULL作为占位符。这就是外层连接的工作原理。
此查询中的技巧是设计联接的匹配条件,使t2必须匹配相同的用户id和更大的日期。它的意思是,如果t2中存在一个日期更大的行,那么t1中与它比较的行就不能是该userid的最大日期。但是如果没有匹配——也就是说,如果t2中不存在比t1中的行日期大的行——我们就知道t1中的行是给定userid中日期最大的行。
在这些情况下(当没有匹配时),t2的列将为NULL——即使是连接条件中指定的列。这就是为什么我们用WHERE t2。UserId IS NULL,因为我们正在搜索没有为给定的UserId找到日期更大的行的情况。
假设Date对于给定的UserID是唯一的,下面是一些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
难道一个qualified子句不是既简单又最好吗?
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
对于上下文,在Teradata这里一个像样的大小测试运行在17秒与这个合格版本和在23秒与“内联视图”/Aldridge解决方案#1。
(T-SQL)首先获取所有用户及其最大日期。与表连接以查找maxdates上用户的对应值。
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
这应该非常简单:
SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)