我有一个表,它是一个关于用户何时登录的集合条目。
username, date, value
--------------------------
brad, 1/2/2010, 1.1
fred, 1/3/2010, 1.0
bob, 8/4/2009, 1.5
brad, 2/2/2010, 1.2
fred, 12/2/2009, 1.3
etc..
我如何创建一个查询,将给我每个用户的最新日期?
更新:我忘记了我需要有一个值与最近的日期。
这是一个简单的老派方法,适用于几乎所有的db引擎,但你必须小心重复:
select t.username, t.date, t.value
from MyTable t
inner join (
select username, max(date) as MaxDate
from MyTable
group by username
) tm on t.username = tm.username and t.date = tm.MaxDate
使用窗口函数将避免由于重复的日期值而导致的任何可能的重复记录问题,所以如果你的db引擎允许它,你可以这样做:
select x.username, x.date, x.value
from (
select username, date, value,
row_number() over (partition by username order by date desc) as _rn
from MyTable
) x
where x._rn = 1
使用窗口函数(适用于Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)
select * from (
select
username,
date,
value,
row_number() over(partition by username order by date desc) as rn
from
yourtable
) t
where t.rn = 1
我使用这种方法获取表中每个用户的最后一条记录。
这是一个查询,根据PDA设备上检测到的最近时间,获得销售人员的最后位置。
CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE())
Group By GS.UserID
GO
select gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
from USERGPS gs
inner join USER s on gs.SalesManNo = s.SalesmanNo
inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate
order by LastDate desc
根据我的经验,最快的方法是取表中没有新行的每一行。
另一个优点是所使用的语法非常简单,而且查询的含义相当容易掌握(取所有行,确保所考虑的用户名不存在更新的行)。
不存在
SELECT username, value
FROM t
WHERE NOT EXISTS (
SELECT *
FROM t AS witness
WHERE witness.username = t.username AND witness.date > t.date
);
ROW_NUMBER
SELECT username, value
FROM (
SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
FROM t
) t2
WHERE rn = 1
内连接
SELECT t.username, t.value
FROM t
INNER JOIN (
SELECT username, MAX(date) AS date
FROM t
GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;
左外连接
SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL
如果您的数据库语法支持它,那么TOP 1 WITH TIES可以与rownumber结合使用。
对于您提供的示例数据,使用以下查询:
SELECT TOP 1 WITH TIES
username, date, value
FROM user_log_in_attempts
ORDER BY ROW_NUMBER() OVER (PARTITION BY username ORDER BY date DESC)
它的收益率:
username | date | value
-----------------------------
bob | 8/4/2009 | 1.5
brad | 2/2/2010 | 1.2
fred | 12/2/2009 | 1.3
Demo
工作原理:
Rownumber () over (partition by…ORDER BY…)对于每个用户名,计算从最年轻的(rownumber=1)到最老的(rownumber=high)的行列表。
按行号排序…将每个用户最年轻的行排序到最上面,然后是每个用户第二年轻的行,依此类推
因为每个用户都有一个最年轻的行,这些最年轻的行在排序条件的意义上是相等的(都有rownumber=1)。所有最年轻的行都将返回。
用SQL-Server测试。