有一張學習打卡表his_sign
表,簡單起見,只設定了兩個字段(id,create_ts)
,乙個是主鍵,另乙個是打卡時間。his_sign
表的資料如下,我們要統計出這張表裡面最長的連續打卡記錄。
id create_ts
------ ---------------------
1 2020-05-01 09:04:26
2 2020-05-02 11:54:45
3 2020-05-04 23:05:03
4 2020-05-06 07:12:31
5 2020-05-06 08:01:52
6 2020-05-07 22:06:48
7 2020-05-08 12:36:58
8 2020-05-09 11:49:13
9 2020-05-12 08:52:35
10 2020-05-13 23:45:57
11 2020-05-14 00:02:24
12 2020-05-14 09:24:18
13 2020-05-19 15:34:45
14 2020-05-21 21:10:02
先檢查資料,我們發現在一天之內可以多次打卡,因此需要先去掉重複打卡的記錄,並將字段create_ts
使用日期格式展示。
select distinct
(date(create_ts)) as create_ts
from
his_sign
去重並格式化後的資料如下:
create_ts
------------
2020-05-01
2020-05-02
2020-05-04
2020-05-06
2020-05-07
2020-05-08
2020-05-09
2020-05-12
2020-05-13
2020-05-14
2020-05-19
2020-05-21
由於資料量不大,我們觀察表資料可知,2020-05-06
到2020-05-09
是最長的序列,總共 4 天。
解題的思路就是把連續的日期編為一組,然後從多組資料中找到數量最多的一組資料,那組資料就是最長的序列。
將表裡面的資料按日期的公升序排序,並給每個日期分配乙個連續的自然數序號,用日期減去它對應的序號,會得到乙個新的日期值。我們發現,連續的日期它們對應的新的日期值為同乙個,因此,這個新的日期值就是序列的組別。
找到連續日期的組的 sql 如下:
with t1 as
(select distinct
(date(create_ts)) as create_ts
from
his_sign),
t2 as
(select
create_ts,
row_number () over (
order by create_ts) as rn
from
t1)
select
create_ts,
date_sub(create_ts, interval rn day) as grp
from
t2
上面 sql 執行後輸出的結果:
create_ts grp
---------- ------------
2020-05-01 2020-04-30
2020-05-02 2020-04-30
2020-05-04 2020-05-01
2020-05-06 2020-05-02
2020-05-07 2020-05-02
2020-05-08 2020-05-02
2020-05-09 2020-05-02
2020-05-12 2020-05-04
2020-05-13 2020-05-04
2020-05-14 2020-05-04
2020-05-19 2020-05-08
2020-05-21 2020-05-09
剩下的操作就簡單多了,把資料最多的那組找出來就對了。只是需要注意,最長的序列有可能有多個,因此在找最長的序列的時候需要注意方法。
結合開窗函式rank() over(order by ***)
可以找到多個最長序列,完整的 sql 如下:
# 1.去掉重複日期,並格式化
with t1 as
(select distinct
(date(create_ts)) as create_ts
from
his_sign),
# 2.給每個日期指定乙個序號
t2 as
(select
create_ts,
row_number () over (
order by create_ts) as rn
from
t1),
# 3.找到分組的依據
t3 as
(select
create_ts,
date_sub(create_ts, interval rn day) as grp
from
t2),
# 4.分組
t4 as
(select
min(create_ts) as start_date,
max(create_ts) as end_date,
count(*) as cnt
from
t3 group by grp),
# 5.對所有序列按照長度降序排序
t5 as
(select
*,rank () over (
order by cnt desc) as rk
from
t4)
# 6.只選擇最長的序列
select
start_date,
end_date,
cnt
from
t5 where rk = 1
輸出:
start_date end_date cnt
---------- ---------- --------
2020-05-06 2020-05-09 4
為了讓大家看得更明白,我用 cte 表示式把每個過程都寫出來了。每段表示式都加了注釋,理解起來應該不難。
注意,上述的 sql 需要在 mysql 8.0 + 環境裡才能正常執行。
sql獲取日期
declare aa varchar 20 set aa convert varchar 100 getdate 23 日 print aa 0 00 00.000 print aa 23 59 59.999 周 print convert varchar 100 dateadd wk,datedi...
SQL當前日期獲取技巧
當前日期 select convert varchar 10 getdate 120 乙個月第一天的sql 指令碼 select dateadd mm,datediff mm,0,getdate 0 本週的星期一 select dateadd wk,datediff wk,0,getdate 0 一...
SQL當前日期獲取技巧
當前日期 select convert varchar 10 getdate 120 乙個月第一天的sql 指令碼 select dateadd mm,datediff mm,0,getdate 0 本週的星期一 select dateadd wk,datediff wk,0,getdate 0 一...