<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
結合order by關鍵詞和limit關鍵詞是可以解決很多的topN問題,比如從二手房資料集中查詢出某個地區的最貴的10套房,從電商交易資料集中查詢出實付金額最高的5筆交易,從學員資訊表中查詢出年齡最小的3個學員等。但是,如果需求變成從二手房資料集中查詢出各個地區最貴的10套房,從電商資料集中查詢出每月實付金額最高的5筆交易,從學員資訊表中查詢出各個科系下年齡最小的3個學員,該如何解決呢?
其實這類問題的核心就是,篩選出組內的topN,而不是從全部資料集中挑選出topN。遇到這種既需要分組也需要排序的問題,直接上開窗函數就能解決了。
開窗函數也叫OLAP函數(Online Analytical Processing,聯機分析處理),主要用來實時分析處理資料。MySQL之前的版本是不支援開窗函數的,從8.0版本之後開始支援開窗函數。
# 開窗函數語法 func_name(<parameter>) OVER([PARTITION BY <part_by_condition>] [ORDER BY <order_by_list> ASC|DESC])
開窗函數語句解析:
函數分為兩部分,一部分是函數名稱,開窗函數的數量比較少,總共才11個開窗函數+聚合函數(所有的聚合函數都可以用作開窗函數)。根據函數的性質,有的需要寫引數,有的不需要寫引數。
另一部分為over語句,over()是必須要寫的,裡面的引數都是非必須引數,可以根據需求有選擇地使用:
開窗函數與分組聚合函數比較相似,都是通過指定欄位將資料分成多份,區別在於:
在這11個開窗函數中,實際工作中用的最多的當屬ROW_NUMBER()、RANK()、DENSE_RANK()這三個排序函數了。下面我們通過一個簡單的資料集學習一下這三個開窗函數。
# 首先建立虛擬的業務員銷售資料 CREATE TABLE Sales ( idate date, iname char(2), sales int ); # 向表中插入資料 INSERT INTO Sales VALUES ('2021/1/1', '丁一', 200), ('2021/2/1', '丁一', 180), ('2021/2/1', '李四', 100), ('2021/3/1', '李四', 150), ('2021/2/1', '劉猛', 180), ('2021/3/1', '劉猛', 150), ('2021/1/1', '王二', 200), ('2021/2/1', '王二', 180), ('2021/3/1', '王二', 300), ('2021/1/1', '張三', 300), ('2021/2/1', '張三', 280), ('2021/3/1', '張三', 280); # 資料查詢 SELECT * FROM Sales; # 查詢各月中銷售業績最差的業務員 SELECT month(idate),iname,sales, ROW_NUMBER() OVER(PARTITION BY month(idate) ORDER BY sales) as sales_order FROM Sales; SELECT * FROM (SELECT month(idate),iname,sales, ROW_NUMBER() OVER(PARTITION BY month(idate) ORDER BY sales) as sales_order FROM Sales) as t WHERE sales_order=1;
# ROW_NUMBER()、RANK()、DENSE_RANK()的區別 SELECT * FROM (SELECT month(idate) as imonth,iname,sales, ROW_NUMBER() OVER(PARTITION BY month(idate) ORDER BY sales) as row_order, RANK() OVER(PARTITION BY month(idate) ORDER BY sales) as rank_order, DENSE_RANK() OVER(PARTITION BY month(idate) ORDER BY sales) as dense_order FROM Sales) as t;
ROW_NUMBER():順序排序——1、2、3
RANK():並列排序,跳過重複序號——1、1、3
DENSE_RANK():並列排序,不跳過重複序號——1、1、2
在實際工作或者面試中,可能會遇到求使用者連續登入天數、連續簽到天數等問題。下面就提供一個用開窗函數解決此類問題的思路。
# 首先建立虛擬的使用者登入表,並插入資料 create table user_login ( user_id varchar(100), login_time datetime ); insert into user_login values (1,'2020-11-25 13:21:12'), (1,'2020-11-24 13:15:22'), (1,'2020-11-24 10:30:15'), (1,'2020-11-24 09:18:27'), (1,'2020-11-23 07:43:54'), (1,'2020-11-10 09:48:36'), (1,'2020-11-09 03:30:22'), (1,'2020-11-01 15:28:29'), (1,'2020-10-31 09:37:45'), (2,'2020-11-25 13:54:40'), (2,'2020-11-24 13:22:32'), (2,'2020-11-23 10:55:52'), (2,'2020-11-22 06:30:09'), (2,'2020-11-21 08:33:15'), (2,'2020-11-20 05:38:18'), (2,'2020-11-19 09:21:42'), (2,'2020-11-02 00:19:38'), (2,'2020-11-01 09:03:11'), (2,'2020-10-31 07:44:55'), (2,'2020-10-30 08:56:33'), (2,'2020-10-29 09:30:28'); # 檢視資料 SELECT * FROM user_login;
計算連續登入天數通常會有以下三種情況:
針對第一種情況:檢視每位使用者連續登入的情況
根據實際經驗,我們知道在一段時間內,使用者可能出現多次連續登入,這些資訊我們都要輸出,所以最後結果輸出的欄位可以是使用者ID、首次登入日期、結束登入日期、連續登入天數這四個。
# 資料預處理:由於統計的視窗期是天數,所以可以對登入時間欄位進行格式轉換,將其變成日期格式然後再去重(去掉使用者同一天內多次登入的情況) # 為方便後續程式碼檢視,將處理結果放置新表中,一步一步操作 create table user_login_date( select distinct user_id, date(login_time) login_date from user_login); # 處理後的資料如下: select * from user_login_date; # 第一種情況:檢視每位使用者連續登陸的情況 # 對使用者登入資料進行排序 create table user_login_date_1( select *, rank() over(partition by user_id order by login_date) irank from user_login_date); #檢視結果 select * from user_login_date_1; # 增加輔助列,幫助判斷使用者是否連續登入 create table user_login_date_2( select *, date_sub(login_date, interval irank DAY) idate #data_sub從指定的日期減去指定的時間間隔 from user_login_date_1); # 檢視結果 select * from user_login_date_2; # 計算每位使用者連續登入天數 select user_id, min(login_date) as start_date, max(login_date) as end_date, count(login_date) as days from user_login_date_2 group by user_id,idate; # ===============【整合程式碼,解決使用者連續登入問題】=================== select user_id, min(login_date) start_date, max(login_date) end_date, count(login_date) days from (select *,date_sub(login_date, interval irank day) idate from (select *,rank() over(partition by user_id order by login_date) irank from (select distinct user_id, date(login_time) login_date from user_login) as a) as b) as c group by user_id,idate;
針對第二種情況:檢視每位使用者最大連續登入的天數
# 計算每個使用者最大連續登入天數 select user_id,max(days) from (select user_id, min(login_date) start_date, max(login_date) end_date, count(login_date) days from (select *,date_sub(login_date, interval irank day) idate from (select *,rank() over(partition by user_id order by login_date) irank from (select distinct user_id, date(login_time) login_date from user_login) as a) as b) as c group by user_id,idate) as d group by user_id;
針對第三種情況:檢視在某個時間段裡連續登入天數超過N天的使用者
假如說,我們的需求是檢視10/29-11/25在這段時間內連續登入天數≥5天的使用者。這個需求也可以用第一種情況查詢的結果進行篩選。
# 檢視在這段時間內連續登入天數≥5天的使用者 select distinct user_id from (select user_id, min(login_date) start_date, max(login_date) end_date, count(login_date) days from (select *,date_sub(login_date, interval irank day) idate from (select *,rank() over(partition by user_id order by login_date) irank from (select distinct user_id, date(login_time) login_date from user_login) as a) as b) as c group by user_id,idate having days>=5 ) as d;
這種寫法是可以得出結果,但是針對這個問題來說有點麻煩了,下面介紹一個簡單的方法:參照一個新的靜態視窗函數lead()
select *, lead(login_date,4) over(partition by user_id order by login_date) as idate5 from user_login_date;
lead函數有三個引數,第一個引數是指定的列(這裡用登陸日期),第二個引數是當前行向後幾行的值,這裡用的是4,也就是第五次登入的日期,第三個引數是如果返回的空值可以用指定值替代,這裡沒有使用第三個引數。 over語句裡面是針對user_id分窗,每個視窗針對登入日期升序。
用第五次登入日期 - login_date+1,如果等於5,說明是連續登入五天的,如果得到空值或者大於5,說明沒有連續登入五天,程式碼和結果如下:
# 計算第5次登入日期與當天的差值 select *,datediff(idate5,login_date)+1 days from (select *,lead(login_date,4) over(partition by user_id order by login_date) idate5 from user_login_date) as a; # 找出相差天數為5的記錄 select distinct user_id from (select *,datediff(idate5,login_date)+1 as days from (select *,lead(login_date,4) over(partition by user_id order by login_date) idate5 from user_logrin_date) as a)as b where days = 5;
【練習】美團外賣平臺資料分析面試題——SQL
現有交易資料表user_goods_table如下:
現在老闆想知道每個使用者購買的外賣品類偏好分佈,並找出每個使用者購買最多的外賣品類是哪個。
# 分析題目:要求輸出欄位為使用者名稱user_name,該使用者購買最多的外賣品類goods_kind # 解題思路:這是一個分組排序的問題,可以考慮視窗函數 # 第一步:使用視窗函數row_number(),對每個使用者購買的外賣品類進行分組統計與排名 select user_name,goods_kind,count(goods_kind), rank() over (partition by user_name order by count(goods_kind) desc) as irank from user_goods_table group by user_name,goods_kind; # 第二步:篩選出每個使用者排名第一的外賣品類 select user_id,goods_kind from (select user_name,goods_kind,count(goods_kind), rank() over (partition by user_name order by count(goods_kind) desc) as irank from user_goods_table group by user_name,goods_kind) as a where irank=1
到此這篇關於MySQL 開窗函數的文章就介紹到這了,更多相關MySQL 開窗函數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45