考虑以下代码:
Select U.[user_id] As UserID
Max(AL.entry_dt) As LastLoginDate
From Users U with (nolock)
Inner Join activity_log AL with (nolock) On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Group By U.[user_id]
Having Max(al.entry_dt) < GetDate() - 30
Order By U.[user_id]
我很好奇是否可以在这里使用Row_Number / Partition?也许是为了让它更有效,或者它是否可以使用?
基本上,我希望每个用户使用最后一个用户登录的最后一个实例,其中用户在过去30天内没有登录。
带来痛苦.....
答案 0 :(得分:3)
要在row_number()
子句中使用where
的结果,请将查询包装在子查询/派生表或common table expression中:
过去30天内登录的用户的原始答案:
select UserId, LastLoginDate
from (
Select
U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
, rn = row_number() over(partition by u.user_id order by AL.entry_dt desc)
From Users U with (nolock)
Inner Join activity_log AL with (nolock)
On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Where AL.entry_dt > GetDate() - 30 -- swapped < for >
) sub
where rn = 1
Order By sub.[userid]
rextester演示:http://rextester.com/XZU40394
返回:
+--------+---------------+
| UserId | LastLoginDate |
+--------+---------------+
| 1 | 2017-09-13 |
| 2 | 2017-09-10 |
| 3 | 2017-09-07 |
+--------+---------------+
为过去30天内未登录的用户更新了答案:
select UserId, LastLoginDate
from (
Select
U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
, rn = row_number() over(partition by u.user_id order by AL.entry_dt desc)
From Users U with (nolock)
Inner Join activity_log AL with (nolock)
On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
) sub
where rn = 1
and lastlogindate < getdate() - 30
Order By [userid]
rextester演示:http://rextester.com/XZU40394
返回:
+--------+---------------+
| UserId | LastLoginDate |
+--------+---------------+
| 4 | 2016-09-13 |
| 6 | 2016-09-10 |
+--------+---------------+
来自测试设置:
create table users (user_id int, external_user bit)
create table activity_log (user_id int, activity_type varchar(32), entry_dt date)
insert into users values (1,1),(2,1),(3,1),(4,1),(5,0),(6,1)
insert into activity_log values
(1,'login','20170913') ,(1,'login','20170912') ,(1,'login','20170911'),(1,'login','20160908')
,(2,'login','20170910') ,(2,'login','20170909') ,(2,'login','20170908')
,(3,'login','20170907') ,(3,'login','20170906') ,(3,'login','20170905')
,(4,'login','20160913') ,(4,'login','20160912') ,(4,'login','20160908')
,(5,'login','20160910') ,(5,'login','20160909') ,(5,'login','20160908')
,(6,'login','20160910') ,(6,'login','20160909') ,(6,'login','20160908')
要更正问题中的查询,请将您的where
移至having
,如下所示:
Select U.[user_id] As UserID
,Max(AL.entry_dt) As LastLoginDate
From Users U with (nolock)
Inner Join activity_log AL with (nolock) On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Group By U.[user_id]
having max(al.entry_dt) < GetDate() - 30
Order By U.[user_id]
答案 1 :(得分:2)
CROSS APPLY或OUTER APPLY允许您从相关查询中为相关表中的每条记录返回n条记录。我认为交叉应用是您想要的,因为如果用户在过去30天内没有登录,您根本不想在结果中看到它们。交叉应用类似于内连接,但为每个记录相关表运行相关查询。 OUTER类似于OUTER join应用,因此它返回相关表中的所有记录,只返回相关查询中匹配的记录。
因此,在下面的示例中,对于每个用户,按entry_dT的降序返回前1条记录。为每个相关用户。外部应用类似于左连接,因此即使没有活动发生,也会返回所有用户。
修改后的演示:http://rextester.com/UQEI69366(以下全部3)再次向SQLZim索取测试人员/数据
SELECT U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
FROM Users U with (nolock)
CROSS APPLY (SELECT top 1 *
FROM activity_log IAL
WHERE U.User_ID = IAL.User_ID
AND IAL.activity_type = 'LOGIN'
ORDER BY IAL.entry_DT Desc) AL
WHERE U.external_user = 1
AND IAL.entry_dt < GetDate() - 30
ORDER BY U.[user_id]
如果你所追求的是过去30天内没有登录的用户...... 一个简单的不存在似乎它会起作用。如果有的话,谁会关心约会时间;您只是在30天内未登录的用户列表之后。
SELECT U.[user_id] As UserID
FROM Users U
WHERE not exists (SELECT *
FROM activity_log IAL
WHERE IAL.activity_type = 'LOGIN'
AND IAL.entry_dt > GetDate() - 30
AND IAL.[user_id] = U.[user_id])
AND U.external_user = 1
ORDER BY U.[user_id]
一个简单的左连接也可以正常工作(返回所有从现在起30天内没有登录的外部用户。
SELECT U.[user_id] As UserID
FROM Users U with (nolock)
LEFT JOIN activity_log AL
ON AL.[user_id] = U.[user_id]
AND AL.activity_type = 'LOGIN'
AND AL.entry_dt > GetDate() - 30
WHERE U.external_user = 1
and AL.user_ID is null
ORDER BY U.[user_id]
答案 2 :(得分:1)
我很好奇是否可以在这里使用Row_Number / Partition?也许是为了使其更有效,或者是否可以使用它?
我更倾向于在你的情况下使用group by而不是行号,因为行号需要额外的索引而不是group by。阅读以下内容了解更多
假设您使用的是您发布的相同查询,下面是所需的索引
for users table ..
create index nci_test on
dbo.usertable(userid,external_login)
对于活动日志表,您需要了解有关数据的更多信息..
<强>实施例强>
如果连接过滤掉比其他更多的行,则索引可以是
create index nci_test1 on
dbo.actvititlog(userid,entry_Dt,activity_type )
如果entry_dt列过滤掉更多行,那么前导列可以是上面索引中的entry_Dt
如果你使用RowNumber,它将需要一个POC索引,你的查询分布在两个表中,所以这不能完成