詳談轉置 pivot

問題概要

  在平常工做中時常會遇到將數據表的行列進行轉換的問題。SQL 提供了靜態轉置的功能 pivot 和 unpivot,但適用範圍很受限,要用 SQL 實現一些比較複雜的轉置功能經常會遇到語句過於複雜的問題,並且缺乏一個標準的解決思路。而集算器的 SPL 語言,則因其語法的靈活和函數庫的豐富,能夠完美地彌補 SQL 在這方面的不足。數據庫

  下面則經過實例詳細闡述一下轉置功能的實現。編程

基礎篇

1、行轉列

一、數據庫的 pivot

  pivot 並非從一開始就存在的功能,只有主流大數據庫廠商較新版本產品,例如 Oralce 11g 以上或 SqlServer2005 以上,才支持這個功能。函數

  從名稱中能夠猜到,這個功能是實現行與列的轉換,也就是將行中的值做爲列名。可是,數據庫的行、列,與普通的表格不同,不能直接將 X 軸與 Y 軸相互對掉就算大功告成。究其緣由,數據庫的列是有惟一性的(也就是列名是不能重複的),而行中存儲的是動態的數據,若是不做爲主鍵,就是能夠重複的。因此,pivot 的實際應用,基本都要跟隨在分組聚合運算以後,經過分組把用於轉置的列(一般都是維度)中每一行數據都處理成不重複的值後,再將各行的值做爲列名來展開。大數據

  從具體應用來看,pivot 的做用,其實就是將某一列的聚合結果,細分爲多個更具體的列的聚合結果,以達到更直觀的視覺效果。spa

  光說概念是否是比較枯燥,不容易理解?下面咱們就以一個具體事例說明,好比 Oracle 數據庫中有一個學生成績表(StudentScore):設計

  001png

  若是想統計每一個班的各科最高分,傳統的作法是:3d

select CLASS,SUBJECT, max(SCORE) THE_HIGHEST_SCORE from STUDENTSCORE group by CLASS,SUBJECTcode

  002png

  上面的結果能夠說觀感很是很差:首先,在「班級」一列裏,一班、二班重複出現,很容易就讓人看錯行;其次,在「科目」一列裏,語文、數學和英語三個科目都放在一塊兒,然而實際上這三個科目的最高分並無什麼比較的意義。blog

  事實上,咱們應該更但願看到如下這樣的結果:
  003png排序

  這個結果中,把科目這一列中的三個科目,各自分離出來單獨做爲一列,既減小了無用的重複,又明確了各科目最高分之間的相互獨立性,看上去清晰明瞭了不少。

  能夠說,pivot 就是爲了這個目的而誕生的,爲了實現上面的結果,如今的查詢寫法以下:

select * from (select CLASS, SUBJECT, SCORE from STUDENTSCORE) pivot (max( SCORE) for SUBJECT in ('Maths' as MAX_MATHS, 'English' as MAX_ENGLISH, 'Chinese' as MAX_CHINSES))

二、集算器的 pivot

  有的同窗可能會問,既然數據庫中已經有了 pivot,那爲何我還須要集算器的 pivot 呢?

  答案是:首先,不是全部的數據庫都提供 pivot;其次,就算全部的數據庫都提供 pivot,但若是是彙總了多個數據庫的數據後還想再來個 pivot?那仍是要用到集算器的 pivot。

  下面咱們來看集算器的 pivot 如何使用

image.png

  代碼說明:
  A1:第一步鏈接數據庫
  A2:第二步提取數據作預處理 (這一步可進一步擴展爲作彙總或聚合等複雜的計算,具體方法請參考相關文章)
  A3:第三步即實現 pivot 的列轉行功能並呈現出來,其效果與 Oracle 的 pivot 是徹底同樣的。
  004png

三、pivot 的其餘意義

  除了數據呈現需求,將行轉爲列後,還可使用列間的計算方法。由於列與行的屬性不一樣,有些列間的計算要在行間實現會比較繁瑣。好比學校對班級成績的某種考覈評比,數、外、語三科的權重分別是:0.六、0.3 和 0.1,用兩個班的三科平均分來計算評比指標:

image.png

  計算結果:
  005png

  上面的計算,假如要在行間實現,則會麻煩許多,有興趣的同窗能夠本身試一下。

2、 列轉行

一、數據庫的 unpivot

  有行轉列,天然就有列轉行。仍是以 Oracle 爲例,它提供的列轉行函數是 unpivot。

  列轉行的功能在業務上又有什麼意義呢?咱們來看這樣一份我的成績表(PersonalScore):

  006png

  若是想知道的是每一個人最擅長哪一個科目(也就是每一個人的哪一科得分最高),行間計算時用 max 函數會很方便,而使用列間計算則相對比較繁瑣。這時 unpivot 函數就派上用場了:

with T1 as (select * from PERSONALSCORE unpivot (SCORE for SUBJECT in (MATHS,ENGLISH, CHINESE))),   
         T2 as (select  NAME  NAME, max(SCORE) The_Highest_Score from T1 group  by  NAME ) select T1.NAME NAME, T1.SUBJECT Good_Subject, T2.The_Highest_Score Good_Score_Score from T1 join T2 on T1.NAME = T2.NAME and T1.SCORE =T2.The_Highest_Score

二、集算器的 pivot@r

  那麼,若是使用的數據庫不是 Oracle 怎麼辦?還須要研究新數據庫的轉置語法細節麼?若是數據庫不支持轉置語句又怎麼辦?須要用 case when 或是子查詢之類的來間接實現相似功能麼?

  沒必要如此煩惱!由於咱們有集算器:

image.png

  計算結果,兩者是同樣的(在排序上可能略有差別):
  007png

  另外,還須要注意一點:數據庫的 unpivot 並不徹底是 pivot 的逆運算,由於 pivot 語句中每每包含了聚合函數,而聚合計算自己是不可逆的,也就是說 unpivot 並不能將 pivot 聚合後的結果再還原回原先的詳細數據。可是集算器的 pivot 由於並不參與聚合計算(聚合計算在 pivot 執行以前已經單獨執行了),因此集算器的 pivot@r 能夠說是集算器的 pivot 運算的逆運算。

高級篇

1、 雙向轉置

  有時須要一些更復雜的轉置操做,好比有這樣一個學生成績表(Score)

  008png

  而咱們想要獲得相似下面結構的學生成績表(含義是查看某個學生某科目的成績變化趨勢):

image.png

  這裏,首先要將數學、語文等列合併成科目列,須要列轉行的操做;而要將學期列拆分紅學期1、學期二等列,須要行轉列的操做。

  考慮到數據表的結構通常是行數遠大於列數,因此咱們能夠先進行列轉行,再進行行轉列。因爲本表的原始數據在行列轉換後數據與轉換前的表中數據能夠一一對應(不須要計算聚合),所以使用集算器的 pivot@r 和 pivot 函數顯然會更方便。

image.png

  運行結果:
  009png

2、 動態列轉置

  上面舉的例子都屬於靜態轉置,要求處理的表格和數據都是「規規矩矩」的。但實際業務中卻總有不那麼守規矩的異類存在,並且相信數量還很多,這時用 SQL 無論是 pivot/unpivot、仍是 case when,仍是別的啥,都有點力不從心……那該怎麼辦?這時集算器的優點就體現出來了:

  好比有下面一個記錄收入狀況的我的收入表(PersonalIncome)

  010png

  但咱們想獲得一個相似下面結構的表

  MANE INCOME_SOURCE_1 INCOMR_AMOUNT_1 INCOME_SOURCE_2 INCOMR_AMOUNT_2 ……

  Zhangsan Wages 8000 Stock 6000 ……

  咱們不肯定行轉列後,列的數量,甚至連列名也不能徹底肯定。這時就不能使用只適用於靜態轉置的 pivot 函數了,而須要使用動態轉置的方法。而集算器的 SPL 語言在動態編程方面,要遠比 SQL 語言靈活得多:

image.png

  結果以下:
  011png

3、 轉置同時列間計算

  假設我有一張關於蔬菜的一週價格清單

  012png

  而我想由此計算得出關於各類蔬菜的一週價格走勢,其中走勢又包含四種狀態:上漲、降低、平穩和初始(週一的值)。

  設計出來的表結構大致以下

  VEGETABLES Monday Tuesday Wednesday Thursday ……

  Eggplant Initial Rise Decline Rise ……

  Cucumber Initial Rise Rise ……

  ……

  雖然須要使用的轉置屬於靜態類型,但在轉置時須要實現列間的計算,這種計算對於 SQL 來講,處理起來很是麻煩。但若使用靈活性更強的集算器的 SPL 語言,則會輕鬆許多:

image.png

  得到「蔬菜的一週價格走勢」表以下
  013png

總結

  相比於 SQL 提供的 pivot 和 unpivot,集算器 SPL 語言所提供的轉置功能要更加靈活,適應性也更加普遍,能夠知足各類複雜的轉置需求。

相關文章
相關標籤/搜索