最近看到一個求助貼,說的是有一張考勤表,表結構爲: 員工編號 NO, 上班日期 DATE ,上班狀態 STATUS ( 0 正常上班 1 加班 2 遲到), 想統計所有員工最近連續加班次數,開始加班日期,結束加班日期,以及最近連續遲到次數,開始遲到日期,結束遲到日期。之前做過類似的統計,比如用戶連續訪問天數,打地鼠連續命中次數等,思路基本相同。這裏做一次詳細記錄:
一、準備數據
1.建表
create table stat_work
(NO CHAR(4) NOT NULL,LOGIN_DATE DATE,STATUS NUMBER(1));
2.插入數據
INSERT INTO STAT_WORK values('1001',to_date('2020-01-17','yyyy-mm-dd'),2);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-16','yyyy-mm-dd'),2);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-16','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-15','yyyy-mm-dd'),2);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-15','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-14','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-14','yyyy-mm-dd'),0);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-13','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-13','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-12','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-12','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1002',to_date('2020-01-11','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-10','yyyy-mm-dd'),1);
INSERT INTO STAT_WORK values('1001',to_date('2020-01-09','yyyy-mm-dd'),0);
commit;
數據如圖:
二、分步查詢數據
主要分以下幾步:
*1.*根據需求,對數據進行篩選。使用排名函數row_number() over()或rank() over()增加僞列,給每行數據添加序號,按no分區,按login_date排序。因爲數據行中login_date無重複數據,所以兩個函數都可以,甚至可以用dense_rank() over()。同時,增加列(login_date-序號),用以標記登陸日期-序號後的差值,命名爲diff_date。
select a.no,
a.login_date,
row_number() over(partition by a.no order by a.login_date) rn,
a.login_date - row_number() over(partition by a.no order by a.login_date) diff_date
from stat_work a
where a.status = 1
執行結果如下圖:
從上圖可以看到,連續日期在減去其序號後的減值是相同的。所以,再根據diff_date進行分組並計數,個數在2以上的,即是連續天數,不管
*2.*在第1步的結果集基礎上,按no和diff_date進行分組統計。其原理是,登陸日期連續,則diff_date相同。
select b.no,
min(b.login_date) 開始日期,
max(b.login_date) 結束日期,
count(*) 連續天數
from (select a.no,
a.login_date,
row_number() over(partition by a.no order by a.login_date) rn,
a.login_date - row_number() over(partition by a.no order by a.login_date) diff_date
from stat_work a
where a.status = 1) b
group by b.no, b.diff_date
having count(*) >= 2
order by 1, 2
查詢結果如下:
說明:這種查詢關鍵是要理解思路和原理。其關鍵點就在於對diff_date的理解。