SQL操作指南九(窗口函數(shù)呐馆、GROUPING)

窗口函數(shù)

窗口函數(shù)也稱為OLAP函數(shù)肥缔,全稱Online Analytical Processing,意為對(duì)數(shù)據(jù)庫(kù)進(jìn)行實(shí)時(shí)分析處理 汹来。

  • 窗口函數(shù)的語(yǔ)法
    語(yǔ)法:
<窗口函數(shù)> OVER ([PARTITION BY <列清單>]
                    ORDER BY <排序用清單列>)

其中重要的關(guān)鍵字是PARTITION和ORDER BY续膳,理解這兩個(gè)關(guān)鍵詞的作用是幫助理解窗口函數(shù)的關(guān)鍵。

能夠作為窗口函數(shù)使用的函數(shù)
窗口函數(shù)大致可以分為以下兩種:
1.能夠作為窗口函數(shù)的聚合函數(shù)(SUM收班、AVG坟岔、COUNT、MAX摔桦、MIN)社付;
2.RANK、DENSE_RANK、ROW_NUMBER等專用窗口函數(shù)瘦穆;

  • 語(yǔ)法的基本使用方法——使用RANK函數(shù)
    RANK函數(shù)如其名纪隙,是用來(lái)計(jì)算記錄排序的函數(shù)。
--各類商品按照銷售單價(jià)從低到高排序
SELECT shop_name,shop_type,sell_price,
RANK () OVER (PARTITION BY shop_type ORDER BY sell_price) AS ranking
FROM shop_list;
+-----------+-----------+------------+---------+
| shop_name | shop_type | sell_price | ranking |
+-----------+-----------+------------+---------+
| 砧板      | 廚房用具  |       2000 |       1 |
| 菜刀      | 廚房用具  |       6000 |       2 |
| 打孔器    | 工具      |        600 |       1 |
| 扳手      | 工具      |       3000 |       2 |
| T恤衫     | 衣服      |       2000 |       1 |
| 運(yùn)動(dòng)T恤   | 衣服      |       3000 |       2 |
+-----------+-----------+------------+---------+

PARTITION BY能夠設(shè)定排序的對(duì)象范圍扛或,示例中我們按照商品的種類進(jìn)行排序。
ORDER BY可以指定按照哪一列碘饼、哪一種順序進(jìn)行排列熙兔。為了按照銷售單價(jià)的升序進(jìn)行排列,我們指定了sell_price艾恼。
窗口函數(shù)中的ORDER BY可以通過(guò)在末尾加上關(guān)鍵字ASC/DESC來(lái)指定升序和降序住涉。省略該關(guān)鍵字默認(rèn)為升序。

  • 無(wú)須指定PARTITION BY
    使用窗口函數(shù)起到關(guān)鍵作用的是PARTITION BY和ORDER BY钠绍。其中舆声,PARTITION BY不是必須的,不指定也是可以正常使用窗口的柳爽。
--不使用PARTITION BY指定列的情況
 SELECT shop_name,shop_type,sell_price,
 RANK () OVER (ORDER BY sell_price) AS ranking
 FROM shop_list;
+-----------+-----------+------------+---------+
| shop_name | shop_type | sell_price | ranking |
+-----------+-----------+------------+---------+
| 打孔器    | 工具      |        600 |       1 |
| T恤衫     | 衣服      |       2000 |       2 |
| 砧板      | 廚房用具  |       2000 |       2 |
| 運(yùn)動(dòng)T恤   | 衣服      |       3000 |       4 |
| 扳手      | 工具      |       3000 |       4 |
| 菜刀      | 廚房用具  |       6000 |       6 |
+-----------+-----------+------------+---------+

不使用PARTITION BY指定列媳握,變成了全部商品的排序。

  • 專用窗口函數(shù)的種類
    如上面排序結(jié)果有兩個(gè)第2位磷脯,因此下一位直接跳過(guò)第3位變成第4位蛾找。有些特殊情況下,可能需要1赵誓,2打毛,3,4這種不需要跳過(guò)的排序俩功,這時(shí)就需要RANK之外的函數(shù)來(lái)實(shí)現(xiàn)了幻枉。
    RANK函數(shù):計(jì)算排序時(shí),如果存在相同的位次的記錄诡蜓,則會(huì)跳過(guò)位次熬甫。
    DENSE_RANK函數(shù):計(jì)算排序時(shí),即使存在相同位次的記錄万牺,也不會(huì)跳過(guò)相同的位次罗珍。
    ROW_NUMBER函數(shù):賦予唯一的連續(xù)位次。
--比較三種函數(shù)的區(qū)別
SELECT shop_name,shop_type,sell_price,
RANK () OVER (ORDER BY sell_price) AS ranking,
DENSE_RANK () OVER (ORDER BY sell_price) AS dense_ranking,
ROW_NUMBER () OVER (ORDER BY sell_price) AS row_num
FROM shop_list;
+-----------+-----------+------------+---------+---------------+---------+
| shop_name | shop_type | sell_price | ranking | dense_ranking | row_num |
+-----------+-----------+------------+---------+---------------+---------+
| 打孔器    | 工具      |        600 |       1 |             1 |       1 |
| T恤衫     | 衣服      |       2000 |       2 |             2 |       2 |
| 砧板      | 廚房用具  |       2000 |       2 |             2 |       3 |
| 運(yùn)動(dòng)T恤   | 衣服      |       3000 |       4 |             3 |       4 |
| 扳手      | 工具      |       3000 |       4 |             3 |       5 |
| 菜刀      | 廚房用具  |       6000 |       6 |             4 |       6 |
+-----------+-----------+------------+---------+---------------+---------+
  • 窗口函數(shù)的適用范圍
    窗口函數(shù)只能在SELECT子句中使用脚粟,另外覆旱,這類函數(shù)不能在WHERE子句或者GROUP BY子句中使用。
    因?yàn)榇翱诤瘮?shù)的目的是排序核无,當(dāng)我們使用WHERE去除了某些記錄或者使用GROUP BY對(duì)記錄進(jìn)行聚合處理扣唱,排序的結(jié)果就是錯(cuò)誤的,因此沒有任何意義。
    所以噪沙,SELECT之外使用窗口函數(shù)是沒有意義的炼彪。

  • 作為窗口函數(shù)使用聚合函數(shù)
    所有的聚合函數(shù)都能用在窗口函數(shù),其語(yǔ)法和專用窗口函數(shù)完全相同正歼。

--將SUM函數(shù)作為窗口函數(shù)
SELECT shop_id,shop_name,sell_price,
SUM(sell_price) OVER (ORDER BY shop_id)AS current_sum
FROM shop_list;
+---------+-----------+------------+-------------+
| shop_id | shop_name | sell_price | current_sum |
+---------+-----------+------------+-------------+
| 0001    | T恤衫     |       2000 | 2000        |
| 0002    | 打孔器    |        600 | 2600        |
| 0003    | 運(yùn)動(dòng)T恤   |       3000 | 5600        |
| 0004    | 菜刀      |       6000 | 11600       |
| 0005    | 砧板      |       2000 | 13600       |
| 0006    | 扳手      |       3000 | 16600       |
+---------+-----------+------------+-------------+
  • 計(jì)算移動(dòng)平均
    窗口函數(shù)就是將表以窗口位單位進(jìn)行分割辐马,并在其中進(jìn)行排序的函數(shù)。其實(shí)其中還包括在窗口中指定更加詳細(xì)的統(tǒng)計(jì)范圍的備選功能局义。該備選功能中的統(tǒng)計(jì)范圍稱為"框架"喜爷。
--指定"最靠近的3行"作為統(tǒng)計(jì)對(duì)象
SELECT shop_id,shop_name,sell_price,
    -> AVG(sell_price) OVER (ORDER BY shop_id ROWS 2 PRECEDING)AS moving_avg
    -> FROM shop_list;
+---------+-----------+------------+------------+
| shop_id | shop_name | sell_price | moving_avg |
+---------+-----------+------------+------------+
| 0001    | T恤衫     |       2000 | 2000.0000  |
| 0002    | 打孔器    |        600 | 1300.0000  |
| 0003    | 運(yùn)動(dòng)T恤   |       3000 | 1866.6667  |
| 0004    | 菜刀      |       6000 | 3200.0000  |
| 0005    | 砧板      |       2000 | 3666.6667  |
| 0006    | 扳手      |       3000 | 3666.6667  |
+---------+-----------+------------+------------+

從編號(hào)0003的數(shù)據(jù)開始數(shù)據(jù)有所不同,我們制定了框架——指定"最靠近的3行"作為統(tǒng)計(jì)對(duì)象萄唇,這里使用了ROWS(行)和PRECEDING(之前)兩個(gè)關(guān)鍵字檩帐,將框架指定為"截止到之前~行",因此ROWS 2 PRECEDING的意思就是截止到之前2行另萤。
由于框架是根據(jù)當(dāng)前記錄決定的湃密,所有和固定的窗口不一樣,其范圍會(huì)隨著當(dāng)前的記錄的變化而變化四敞。
自身(當(dāng)前記錄)→之前1行的記錄→之前2行的記錄
這樣的統(tǒng)計(jì)方法稱為移動(dòng)平均泛源。由于這種方法在希望實(shí)時(shí)把握最近狀態(tài)時(shí)非常方便,所以常常會(huì)應(yīng)用在對(duì)股市趨勢(shì)的實(shí)時(shí)跟蹤中目养。
使用關(guān)鍵字FOLLOWING(之后)替換PRECEDING俩由,就可以指定"截至到之后~行"作為框架了。

  • 兩個(gè)ORDER BY
    窗口函數(shù)中的ORDER BY只是對(duì)排序的序號(hào)進(jìn)行了排列癌蚁,那么如何對(duì)排序做降序呢幻梯?
    很簡(jiǎn)單,在末尾加上ORDER BY子句就行了努释。
 SELECT shop_id,shop_name,sell_price,
 RANK() OVER (ORDER BY sell_price)AS ranking
 FROM shop_list
 ORDER BY ranking DESC;
+---------+-----------+------------+---------+
| shop_id | shop_name | sell_price | ranking |
+---------+-----------+------------+---------+
| 0004    | 菜刀      |       6000 |       6 |
| 0003    | 運(yùn)動(dòng)T恤   |       3000 |       4 |
| 0006    | 扳手      |       3000 |       4 |
| 0001    | T恤衫     |       2000 |       2 |
| 0005    | 砧板      |       2000 |       2 |
| 0002    | 打孔器    |        600 |       1 |
+---------+-----------+------------+---------+

GROUPING運(yùn)算符

  • 同時(shí)計(jì)算出合計(jì)值
    我們?cè)谑褂肎ROUP BY子句合計(jì)各個(gè)種類的總價(jià)的時(shí)候碘梢,會(huì)得到如下的表:
+-----------+-----------------+
| shop_type | sum(sell_price) |
+-----------+-----------------+
| 衣服      | 5000            |
| 工具      | 3600            |
| 廚房用具  | 8000            |
+-----------+-----------------+

但是如果想要將這幾個(gè)種類合計(jì)到一起,那么就需要UNION ALL將他們連接到一起了伐蒂。

SELECT '合計(jì)' AS shop_type,SUM(sell_price)
FROM shop_list
UNION ALL
SELECT shop_type,SUM(sell_price)
FROM shop_list
GROUP BY shop_type;
+-----------+-----------------+
| shop_type | sum(sell_price) |
+-----------+-----------------+
| 合計(jì)      | 16600           |
| 衣服      | 5000            |
| 工具      | 3600            |
| 廚房用具  | 8000            |
+-----------+-----------------+

這樣雖然能計(jì)算出我們想要的結(jié)果煞躬,但是需要執(zhí)行兩次相同的SELECT語(yǔ)句,再將其結(jié)果連接逸邦,不僅繁瑣恩沛,而且DBMS內(nèi)部處理成本也非常高。因此缕减,我們需要試著找出其他更高效的方法雷客。

  • ROLLUP——同時(shí)計(jì)算出合計(jì)值和小計(jì)值
    為了滿足類似上面的要求,標(biāo)準(zhǔn)SQL引入了GROUPING運(yùn)算符桥狡,GROUPING運(yùn)算符主要包括以下幾種:
    1.ROLLUP
    2.CUBE
    3.GROUPING SETS

ROLLUP的用法

SELECT shop_type,SUM(sell_price) AS sum_price
FROM shop_list
GROUP BY shop_type WITH ROLLUP;
+-----------+-----------+
| shop_type | sum_price |
+-----------+-----------+
| 廚房用具  | 8000      |
| 工具      | 3600      |
| 衣服      | 5000      |
| NULL      | 16600     |
+-----------+-----------+

該運(yùn)算符的作用搅裙,就是"一次計(jì)算出不同聚合鍵的組合結(jié)果"皱卓,其計(jì)算結(jié)果默認(rèn)使用NULL作為聚合鍵。

將"登記日期"添加到聚合鍵中

--不使用ROLLUP的結(jié)果
SELECT shop_type,register_date,SUM(sell_price) AS sum_price
FROM shop_list
GROUP BY shop_type,register_date;
+-----------+---------------+-----------+
| shop_type | register_date | sum_price |
+-----------+---------------+-----------+
| 衣服      | 2009-09-20    | 5000      |
| 工具      | 2009-09-20    | 3600      |
| 廚房用具  | 2009-09-20    | 8000      |
+-----------+---------------+-----------+
--使用ROLLUP的結(jié)果
SELECT shop_type,register_date,SUM(sell_price) AS sum_price
FROM shop_list
GROUP BY shop_type,register_date WITH ROLLUP;
+-----------+---------------+-----------+
| shop_type | register_date | sum_price |
+-----------+---------------+-----------+
| 廚房用具  | 2009-09-20    | 8000      |
| 廚房用具  | NULL          | 8000      |    ←小計(jì)
| 工具      | 2009-09-20    | 3600      |
| 工具      | NULL          | 3600      |
| 衣服      | 2009-09-20    | 5000      |
| 衣服      | NULL          | 5000      |
| NULL      | NULL          | 16600     | ←合計(jì)
+-----------+---------------+-----------+

將兩次結(jié)果做比較之后部逮,發(fā)現(xiàn)使用ROLLUP多出了對(duì)每個(gè)類別的小計(jì)(使用了未登記日期作為聚合鍵)娜汁。

  • GROUPING函數(shù)——讓NULL更加容易分辨
    在使用ROLLUP的時(shí)候,出現(xiàn)了幾處NULL兄朋,當(dāng)日期本身為NULL時(shí)掐禁,那么NULL代表的意義就不太明確了。為了避免混淆颅和,SQL提供了一個(gè)用來(lái)判斷超級(jí)分組記錄為NULL的特定函數(shù)——GROUPING函數(shù)穆桂。該函數(shù)在其參數(shù)列的值為超級(jí)分組記錄所產(chǎn)生的NULL時(shí)返回1,其他情況返回0融虽。
 SELECT GROUPING(shop_type)AS shop_type,GROUPING(register_date)AS  register_date,SUM(sell_price)AS sum_price
FROM shop_list
GROUP BY shop_type,register_date WITH ROLLUP;
+-----------+---------------+-----------+
| shop_type | register_date | sum_price |
+-----------+---------------+-----------+
|         0 |             0 | 8000      |
|         0 |             1 | 8000      |
|         0 |             0 | 3600      |
|         0 |             1 | 3600      |
|         0 |             0 | 5000      |
|         0 |             1 | 5000      |
|         1 |             1 | 16600     |   ←超級(jí)分組產(chǎn)生的NULL返回1
+-----------+---------------+-----------+

該表對(duì)應(yīng)上面ROLLUP產(chǎn)生的表,查找NULL的位置就能知道是不是超級(jí)分組產(chǎn)生的了灼芭。

使用GROUPING函數(shù)還能在超級(jí)分組記錄的鍵中插入字符串有额。也就是說(shuō),當(dāng)GROUPING函數(shù)返回值為1時(shí)彼绷,指定”合計(jì)“和”小計(jì)“等字符串巍佑,其他情況返回正常的列的值。

SELECT CASE WHEN GROUPING(shop_type)=1
            THEN '商品種類合計(jì)'
            ELSE shop_type END AS shop_type,
            CASE WHEN GROUPING(register_date)=1
            THEN '登記日期合計(jì)'
            ELSE register_date END AS register_date,
            SUM(sell_price) AS sum_price
FROM shop_list
GROUP BY shop_type,register_date WITH ROLLUP;
+--------------+---------------+-----------+
| shop_type    | register_date | sum_price |
+--------------+---------------+-----------+
| 廚房用具     | 2009-09-20     | 8000      |
| 廚房用具     | 登記日期合計(jì)    | 8000      |
| 工具         | 2009-09-20    | 3600      |
| 工具         | 登記日期合計(jì)   | 3600      |
| 衣服         | 2009-09-20    | 5000      |
| 衣服         | 登記日期合計(jì)   | 5000      |
| 商品種類合計(jì) | 登記日期合計(jì)    | 16600     |
+--------------+---------------+-----------+
  • CUBE——用數(shù)據(jù)搭積木
    CUBE是立方體的意思寄悯,和ROLLUP一樣萤衰,生動(dòng)地說(shuō)明了其作用。CUBE和ROLLUP的語(yǔ)法一樣猜旬,只要將ROLLUP用CUBE代替就行了脆栋。

  • GROUPING SETS——取得期望的積木
    該運(yùn)算符可以從ROLLUP和CUBE結(jié)果中取得部分記錄(即GROUPING返回值為1的數(shù)據(jù))。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末洒擦,一起剝皮案震驚了整個(gè)濱河市椿争,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熟嫩,老刑警劉巖秦踪,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掸茅,居然都是意外死亡椅邓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門昧狮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)景馁,“玉大人,你說(shuō)我怎么就攤上這事陵且〔蒙” “怎么了个束?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)聊疲。 經(jīng)常有香客問(wèn)我茬底,道長(zhǎng),這世上最難降的妖魔是什么获洲? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任阱表,我火速辦了婚禮,結(jié)果婚禮上贡珊,老公的妹妹穿的比我還像新娘最爬。我一直安慰自己,他們只是感情好门岔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布爱致。 她就那樣靜靜地躺著,像睡著了一般寒随。 火紅的嫁衣襯著肌膚如雪糠悯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天妻往,我揣著相機(jī)與錄音互艾,去河邊找鬼。 笑死讯泣,一個(gè)胖子當(dāng)著我的面吹牛纫普,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播好渠,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昨稼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了晦墙?” 一聲冷哼從身側(cè)響起悦昵,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晌畅,沒想到半個(gè)月后但指,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抗楔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年棋凳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片连躏。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剩岳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出入热,到底是詐尸還是另有隱情拍棕,我是刑警寧澤晓铆,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绰播,受9級(jí)特大地震影響骄噪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蠢箩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一链蕊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谬泌,春花似錦滔韵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贱鼻,卻和暖如春囱皿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背忱嘹。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耕渴,地道東北人拘悦。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像橱脸,于是被迫代替她去往敵國(guó)和親础米。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容