巧用分析函式循序漸進解決實際問題
今天同事問我一個問題,他說問題的邏輯很清晰,但是感覺無從開始。問題的邏輯大體是這樣的。
有一個表,存在著大量的資料,比如account_id為1代表account的編號,可以把這個account做暫停操作,相當於把賬戶凍結,然後在一定的時候後做恢復操作,相當於把賬戶解凍。就對應ACTIVITY_CODE的 SUSPEND,RESUME
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
現在想統計這些操作的累計時間,比如對於ACCOUNT_ID為1的賬戶來說,存在3組操作。
SUSPEND--RESUME 28-DEC-14 ~ 01-JAN-15 耗時4天
SUSPEND--RESUME 03-JAN-15 ~ 06-JAN-15 耗時3天
SUSPEND--RESUME 07-JAN-15 ~ 08-JAN-15 耗時1天
其中SUSPEND和RESUME操作是一組操作,RESUME操作依賴於SUSPEND,這一點很容易理解,要解凍賬號,首先賬號得已經凍結。
所以對於上面的操作來說我們期望得到對於ACCOUNT_ID為1的賬戶來說,耗時8天。
如果單純得到最大值減去最小值得到的時間差是不準確的,畢竟每組操作的時間是不連續的。
我使用瞭如下的語句進行了簡單模擬。
首先建立表test
create table test(account_id number,activity_code varchar2(30),effective_date date);
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
2 SUSPEND 30-DEC-14
2 RESUME 02-JAN-15
2 SUSPEND 04-JAN-15
2 RESUME 05-JAN-15
2 SUSPEND 07-JAN-15
2 RESUME 08-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
這個時候我們對資料進行基本的排序,就能夠清晰的看到這些操作的時間。
select *from test order by account_id,effective_date;
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
2 SUSPEND 30-DEC-14
2 RESUME 02-JAN-15
2 SUSPEND 04-JAN-15
2 RESUME 05-JAN-15
2 SUSPEND 07-JAN-15
2 RESUME 08-JAN-15
2 (
3 select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
4 )t
5 /
ROWNUM CHK_NUM ACCOUNT_ID DURATION
---------- ---------- ---------- ----------
1 1 1 4
2 0 1 2
3 1 1 3
4 0 1 1
5 1 1 1
6 0 1 0
7 1 2 3
8 0 2 2
9 1 2 1
10 0 2 2
11 1 2 1
12 0 2 0
對於mod(ronum,2)=0的資料行來說,這些資料可以排除,這些時間差是不需要考慮的。
最後的完整sql如下:
select t.account_id,sum(duration)
from
(
select rownum,mod(rownum,2)chk_num,t.account_id,next_date-effective_date duration from
(
select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
)t
)t where chk_num=1
group by account_id;
ACCOUNT_ID SUM(DURATION)
---------- -------------
1 8
2 5
可以看到account_id為1的賬戶耗時8天,account_id為2的賬戶耗時5天。
在表中含有大量的資料前提下,這個操作的資源消耗也不高。如果採用pl/sql也可以解決,不過可能程式碼要更多。
有一個表,存在著大量的資料,比如account_id為1代表account的編號,可以把這個account做暫停操作,相當於把賬戶凍結,然後在一定的時候後做恢復操作,相當於把賬戶解凍。就對應ACTIVITY_CODE的 SUSPEND,RESUME
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
現在想統計這些操作的累計時間,比如對於ACCOUNT_ID為1的賬戶來說,存在3組操作。
SUSPEND--RESUME 28-DEC-14 ~ 01-JAN-15 耗時4天
SUSPEND--RESUME 03-JAN-15 ~ 06-JAN-15 耗時3天
SUSPEND--RESUME 07-JAN-15 ~ 08-JAN-15 耗時1天
其中SUSPEND和RESUME操作是一組操作,RESUME操作依賴於SUSPEND,這一點很容易理解,要解凍賬號,首先賬號得已經凍結。
所以對於上面的操作來說我們期望得到對於ACCOUNT_ID為1的賬戶來說,耗時8天。
如果單純得到最大值減去最小值得到的時間差是不準確的,畢竟每組操作的時間是不連續的。
我使用瞭如下的語句進行了簡單模擬。
首先建立表test
create table test(account_id number,activity_code varchar2(30),effective_date date);
插入一些資料,為了儘量使資料看起來不是很規整,把資料的插入順序進行了微調。
insert into test values(1,'SUSPEND',sysdate-12);
insert into test values(1,'RESUME',sysdate-8);
insert into test values(1,'SUSPEND',sysdate -6);
insert into test values(1,'RESUME',sysdate-3);
insert into test values(2,'SUSPEND',sysdate-10);
insert into test values(2,'RESUME',sysdate-7);
insert into test values(2,'SUSPEND',sysdate -5);
insert into test values(2,'RESUME',sysdate-4);
insert into test values(2,'SUSPEND',sysdate-2);
insert into test values(2,'RESUME',sysdate-1);
insert into test values(1,'SUSPEND',sysdate-2);
insert into test values(1,'RESUME',sysdate-1);
這樣我們得到了一個基本的列表。代表某個賬戶在某個時間段進行了某些操作。insert into test values(1,'SUSPEND',sysdate-12);
insert into test values(1,'RESUME',sysdate-8);
insert into test values(1,'SUSPEND',sysdate -6);
insert into test values(1,'RESUME',sysdate-3);
insert into test values(2,'SUSPEND',sysdate-10);
insert into test values(2,'RESUME',sysdate-7);
insert into test values(2,'SUSPEND',sysdate -5);
insert into test values(2,'RESUME',sysdate-4);
insert into test values(2,'SUSPEND',sysdate-2);
insert into test values(2,'RESUME',sysdate-1);
insert into test values(1,'SUSPEND',sysdate-2);
insert into test values(1,'RESUME',sysdate-1);
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
2 SUSPEND 30-DEC-14
2 RESUME 02-JAN-15
2 SUSPEND 04-JAN-15
2 RESUME 05-JAN-15
2 SUSPEND 07-JAN-15
2 RESUME 08-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
select *from test order by account_id,effective_date;
ACCOUNT_ID ACTIVITY_CODE EFFECTIVE_DATE
---------- ------------------------------ ------------------
1 SUSPEND 28-DEC-14
1 RESUME 01-JAN-15
1 SUSPEND 03-JAN-15
1 RESUME 06-JAN-15
1 SUSPEND 07-JAN-15
1 RESUME 08-JAN-15
2 SUSPEND 30-DEC-14
2 RESUME 02-JAN-15
2 SUSPEND 04-JAN-15
2 RESUME 05-JAN-15
2 SUSPEND 07-JAN-15
2 RESUME 08-JAN-15
我們進行重要的一步操作,把操作進行分組,即SUSPEND-RESUME的操作合成一些,可以使用分析函式lead來完成。
比如對於
select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
2 /
ACCOUNT_ID EFFECTIVE_DATE NEXT_DATE
---------- ------------------ ------------------
1 28-DEC-14 01-JAN-15
1 01-JAN-15 03-JAN-15
1 03-JAN-15 06-JAN-15
1 06-JAN-15 07-JAN-15
1 07-JAN-15 08-JAN-15
1 08-JAN-15 08-JAN-15
2 30-DEC-14 02-JAN-15
2 02-JAN-15 04-JAN-15
2 04-JAN-15 05-JAN-15
2 05-JAN-15 07-JAN-15
2 07-JAN-15 08-JAN-15
2 08-JAN-15 08-JAN-15
SQL> select rownum,mod(rownum,2)chk_num,t.account_id,next_date-effective_date duration from 比如對於
select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
2 /
ACCOUNT_ID EFFECTIVE_DATE NEXT_DATE
---------- ------------------ ------------------
1 28-DEC-14 01-JAN-15
1 01-JAN-15 03-JAN-15
1 03-JAN-15 06-JAN-15
1 06-JAN-15 07-JAN-15
1 07-JAN-15 08-JAN-15
1 08-JAN-15 08-JAN-15
2 30-DEC-14 02-JAN-15
2 02-JAN-15 04-JAN-15
2 04-JAN-15 05-JAN-15
2 05-JAN-15 07-JAN-15
2 07-JAN-15 08-JAN-15
2 08-JAN-15 08-JAN-15
這樣就把操作進行了一個初步的分組,但是標黃列的資料是錯誤的,它統計的是RESUME-SUSPEND的時間差,這個和邏輯不符,我們需要得到的是SUSPEND-RESUME的時間差。我們可以進行簡單過濾。
2 (
3 select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
4 )t
5 /
ROWNUM CHK_NUM ACCOUNT_ID DURATION
---------- ---------- ---------- ----------
1 1 1 4
2 0 1 2
3 1 1 3
4 0 1 1
5 1 1 1
6 0 1 0
7 1 2 3
8 0 2 2
9 1 2 1
10 0 2 2
11 1 2 1
12 0 2 0
對於mod(ronum,2)=0的資料行來說,這些資料可以排除,這些時間差是不需要考慮的。
最後的完整sql如下:
select t.account_id,sum(duration)
from
(
select rownum,mod(rownum,2)chk_num,t.account_id,next_date-effective_date duration from
(
select t.account_id,effective_date,lead(effective_date,1,effective_date) over(partition by account_id order by account_id,effective_date) next_date from test t
)t
)t where chk_num=1
group by account_id;
ACCOUNT_ID SUM(DURATION)
---------- -------------
1 8
2 5
可以看到account_id為1的賬戶耗時8天,account_id為2的賬戶耗時5天。
在表中含有大量的資料前提下,這個操作的資源消耗也不高。如果採用pl/sql也可以解決,不過可能程式碼要更多。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8494287/viewspace-1405503/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Docker循序漸進Docker
- 【深度長文】循序漸進解讀Oracle AWR效能分析報告Oracle
- 循序漸進學加密加密
- 循序漸進linux(二)Linux
- SQL開發 循序漸進SQL
- 循序漸進學習oracleOracle
- Linux循序漸進(09)(轉)Linux
- Linux循序漸進(08)(轉)Linux
- Linux循序漸進(04)(轉)Linux
- Linux循序漸進(07)(轉)Linux
- Linux循序漸進(06)(轉)Linux
- Linux循序漸進(05)(轉)Linux
- Linux循序漸進(03)(轉)Linux
- Linux循序漸進(02)(轉)Linux
- Linux循序漸進(01)(轉)Linux
- Linux循序漸進(16)(轉)Linux
- Linux循序漸進(13)(轉)Linux
- Linux循序漸進(11)(轉)Linux
- Linux循序漸進(10)(轉)Linux
- 循序漸進 Redis 分散式鎖(以及何時不用它)Redis分散式
- 循序漸進的用js實現一個bind()JS
- 循序漸進DIY一個react(二)React
- 循序漸進DIY一個react(一)React
- 循序漸進DIY一個react(三)React
- 循序漸進DIY一個react(四)React
- 循序漸進理解TypeScript型別模式TypeScript型別模式
- Linux循序漸進(22):vi(轉)Linux
- Linux循序漸進(19):shell(轉)Linux
- 理論實踐:循序漸進理解AWR細緻入微分析效能報告
- 怎樣黑進Microsoft:循序漸進指南 (轉)ROS
- CRM專案成功實施在於循序漸進(轉)
- CRM專案成功實施在於循序漸進 (轉)
- 循序漸進Oracle - 全面認識Oracle ASHOracle
- SOA治理最佳策略:小幅起步循序漸進
- Jsp+JavaBean循序漸進教程(六)JSJavaBean
- Linux循序漸進(24):vi命令(轉)Linux
- Linux循序漸進(21):別名(轉)Linux
- Linux循序漸進(17):列印操作(轉)Linux