在工作中,我們經常會遇到循環計算的需求。例如,計算一日留存率、二日留存率、……、N日留存率。如果N值不大,我們雖然可以通過合併多次查詢結果來解決,但代碼可讀性會差很多,而且運行速度也不快。
在《數據分析SQL必修教程,一個案例讓你明明白白》一文中我們就已經拋出來這個問題了,今天我們就利用存儲過程來解決這一類問題。
01什麼是存儲過程
存儲過程是實現經過編譯並存儲在資料庫中的一段SQL語句集合。它的思想很簡單,從名字中就可以看出來,它的主要功能就是進行數據的存儲。與普通增刪改查語句不同的是,它通過增加變量、控制等方法來豐富了進行存儲的條件。
簡單來說,當我們要插入10條數據時,要複製insert語句十次,但如果加入存儲過程的循環語句,一次就夠了。具有Java、Python編程基礎的人會很容易理解這一點。作為一種方法,就必要具有優點和缺點的。
(1)優點
存儲過程在創造時就進行編譯,以後每次執行都不需再重新編譯,從而提高執行速度;可重複使用,簡化資料庫開發人員工作;安全性高,可設定用戶的使用權。(2)缺點
往往定製化於特定的資料庫上,當切換到其它資料庫系統時,需重寫原有的存儲過程;性能調校與撰寫受限於資料庫系統。
02案例分析
本文仍是選用阿里天池中的淘寶用戶行為數據集進行介紹。
(1)數據集說明
該數據集記錄了2019-11-28至2019-12-03期間的行為數據,數據集中具有如下10個欄位,每個欄位均有注釋。
我們需要得到如下結果表。
(2)解決思路
創建如下的中間表;
寫出計算間隔天數為N的留存查詢語句,並利用存儲過程對間隔天數進行循環,每次循環結果寫入中間表中;對中間表進行行轉列,得到結果表。
03代碼呈現
接下來看代碼
(1)創建中間表
createtableretention(idint auto_increment, datevarchar(20) defaultnull, new_users varchar(20)defaultnull, nday_after intdefaultnull, retention_number intdefaultnull, primary key(id)); --建立中間表
(2)編寫存儲過程
truncate retention;
drop procedure if exists cal_retention;
delimiter $$
create procedure cal_retention (in nday int)
begin
set @i=1;
while @i<=nday do
insert intoretention(date,new_users,nday_after,retention_number)
select bh1.date ,count(distinct bh1.user_id) as new_users,
@i as nday_after,
count(distinct bh2.user_id) asretention_number
from
(select *
from(select *,from_unixtime(time_stamp) as datetime,
from_unixtime(time_stamp,'%y-%m-%d') as date,
from_unixtime(time_stamp,'%h:%i:%s') as time,
row_number() over(partition by user_id order by time_stamp asc) as rn
from behavior
where 1=1)t1
where rn=1) bh1
left join behavior bh2 on bh1.user_id=bh2.user_id and datediff(from_unixtime(bh2.time_stamp,'%y-%m-%d'),bh1.date)=@i
where 1=1
groupby bh1.date;
set @i=@i+1;
end while;
end $$
delimiter;
call cal_retention(2)
執行得到中間表。
(3)行轉列得到結果表
執行得到結果表。
04結語
雖然實際中數據分析師往往會藉助Python或R語言來解決類似的問題,但掌握存儲過程原理、知道存儲過程用途,對於數據分析師在和資料庫開發工程師打交道也是非常有幫助的!