TSQL--查找連續登陸用戶


--==========================================

需求:有一個用戶登陸日志表,記錄用戶每次登陸時間,然后想查找用戶按天連續登陸的情況,找出每次連續登陸的最早時間和最后時間以及連續登陸天數。

--===========================================

由於長久未寫此類SQL,有點手生,本着走一步算一步的精神,慢慢來。

首先查看日志表

SELECT [Uid]
      ,[loginDate]
  FROM [dbo].[Member_LoginLog]
  WHERE [UID]=268

由於按天計算連續登陸,表中時間精確到毫秒,很難肉眼看出數據是否連續,於是考慮轉換數據

而又由於我們只關心最早登陸時間和最后登陸時間,因此我們可以先按照天來統計用戶最早登陸時間和最后登陸時間,並將時間轉換成對應天數

--==============================================
--統計出用戶每天最早登陸時間和最后登陸時間
SELECT T1.[UID]
,DATEDIFF(DAY,'2014-01-01',LoginDate) AS DiffDays
,MAX(LoginDate) AS MaxLoginDate
,MIN(LoginDate) AS MinLoginDate
INTO [dbo].[Member_LoginLog_Status1]
FROM [dbo].[Member_LoginLog] T1
GROUP BY T1.[UID],DATEDIFF(DAY,'2014-01-01',LoginDate)
--======================================
--查看效果
SELECT [UID]
,[DiffDays]
,[MaxLoginDate]
,[MinLoginDate]
FROM [dbo].[Member_LoginLog_Status1]
WHERE UID=268

從上圖很容易看出第二天沒連續登陸,是不是很容易看啊

接下來就是查找聯系的天數了,如果我們按照UID分組,然后對DiffDays來排序求出排名來,依據DiffDays的增長量和RID量便可以判斷出天數是否連續

SELECT 
ROW_NUMBER()OVER(PARTITION BY UID ORDER BY [DiffDays] ASC) AS RID,
T1.*
FROM [dbo].[Member_LoginLog_Status1] T1
WHERE [UID]=268

這樣我們便可以使用表的自連接來查找連續的登錄,由於需要按照用戶和天數來算出排名,因此我們可以先建立索引

CREATE CLUSTERED INDEX CIX_UID_Days ON 
[dbo].[Member_LoginLog_Status1]
(
    [UID],[DiffDays]
)

然后再求連續區間:

--==========================================
--查找連續的登錄
;WITH Tem AS(
SELECT 
ROW_NUMBER()OVER(PARTITION BY UID ORDER BY [DiffDays] ASC) AS RID,
T1.*
FROM [dbo].[Member_LoginLog_Status1] T1
)
,Tem1 AS(
SELECT ROW_NUMBER()OVER(
    PARTITION BY T1.[UID],T1.[DiffDays] 
    ORDER BY T2.[diffdays]-T1.[diffdays] DESC) AS RID,
T1.[UID],
T1.MinLoginDate,
T2.MaxLoginDate,
T1.[diffdays] AS MinDiffDays,
T2.[diffdays] AS MAXDiffDays
FROM Tem AS T1
INNER JOIN Tem AS T2
ON T1.UID=T2.UID
AND T1.[diffdays]<=T2.[diffdays]
AND T2.[diffdays]-T1.[diffdays]= T2.RID-T1.RID
)
SELECT 
[UID],
MinLoginDate,
MaxLoginDate,
MinDiffDays,
MAXDiffDays
INTO [dbo].[Member_LoginLog_Status2]
FROM Tem1 AS T1
WHERE T1.RID=1
--=========================================
--檢查結果
SELECT [UID]
,[MinLoginDate]
,[MaxLoginDate]
,[MinDiffDays]
,[MAXDiffDays]
FROM [dbo].[Member_LoginLog_Status2]
WHERE [UID]=268

找出連續的區間后,我們會發現有很多區間不是最大連續區間,如第5天到第17天連續,但是比之更大的區間還有第3天到第17天,對於這種問題,解決辦法就是依據maxDiffDays分組,求出最小的minDiffDays

由於此時要按照用戶和maxDiffDays分組,然后按照MinDiffDays排序求最小值,因此先建立索引

CREATE CLUSTERED INDEX CIX_UID_MAXDiffDays
ON [AccMain_101].[dbo].[Member_LoginLog_Status2]
([UID],MAXDiffDays,MinDiffDays ASC)

然后再查詢:

--====================================
--求出最大連續區間
;WITH CTE1 AS(
SELECT 
ROW_NUMBER()OVER(PARTITION BY [UID],MAXDiffDays ORDER BY MinDiffDays ASC) AS RID,
[UID],
MinLoginDate,
MaxLoginDate,
MinDiffDays,
MAXDiffDays
FROM [AccMain_101].[dbo].[Member_LoginLog_Status2] AS T1
)
INSERT INTO [dbo].[Member_LoginLog_Status3]
           ([Uid]
           ,[firstLoginDate]
           ,[lastLoginDate]
           ,[loginNumber])
SELECT [UID],
MinLoginDate,
MaxLoginDate,
T1.MAXDiffDays-MinDiffDays AS ContinueDays
FROM CTE1 T1
WHERE T1.RID=1
--==================================
--查看結果
SELECT  [Uid]
      ,[firstLoginDate]
      ,[lastLoginDate]
      ,[loginNumber]
  FROM [dbo].[Member_LoginLog_Status3]
  WHERE [UID]=268

查詢結果:

結果正是我們想要的,因此打完收工,回家吃飯。

--===============================================

總結:其實查找連續或查找孤島這類原理,都是利用自連接然后看增長是否連續,多折騰幾遍就好。

--===============================================

wwwwgou的回復中,指出一條更快捷的計算方式,同樣使用排名來計算,但不使用關聯,而是計算排名與登陸天數的差值,如果登陸天數連續增長,則排名也連續增長,兩者的差值保持不變;如果登陸天數不連續,則登陸天數增長的值就會比排名增長的值高,這時兩者的差值就會變大。

如下圖:

隨着天數不連續的次數增加,[天數-排名]的值會不斷增大,因此可以使用[天數-排名]來分組,便可以定位到連續區間。

PS: 不會出現兩個不同連續區間的[天數-排名]值一樣的情況

查找代碼:

--========================================
--感謝wwwwgou提供,
--此代碼已略做修改
SELECT
[Uid],
mindt = MIN(mindt),
maxdt = MAX(maxdt),
logdays = COUNT(*)
FROM 
(
SELECT
[Uid],
RowNo = ROW_NUMBER() 
OVER(PARTITION BY [Uid] 
ORDER BY DATEDIFF(DAY,'2014-01-01', loginDate)),
DiffDay = DATEDIFF(DAY,'2014-01-01', loginDate),
mindt = MIN(loginDate),
maxdt = MAX(loginDate)
FROM dbo.Member_LoginLog
GROUP BY [Uid], DATEDIFF(DAY,'2014-01-01', loginDate)
) T
GROUP BY [Uid], [RowNo] - DiffDay
ORDER BY [Uid], minDt

對wwwwgou筒子再次表示嬸嬸地感謝。

--===============================================

請原諒我蒼白的講解,讓您們只能看代碼。

妹子騷猴就上,不要着急。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM