MathJax

MathJax-2

MathJax-3

Google Code Prettify

置頂入手筆記

EnterproseDB Quickstart — 快速入門筆記

由於考慮採用 EnterpriseDB 或是直接用 PostgreSQL 的人,通常需要一些入手的資料。這邊紀錄便提供相關快速上手的簡單筆記 ~ 這篇筆記以 資料庫安裝完畢後的快速使用 為目標,基本紀錄登入使用的範例:

2017年7月4日 星期二

在 PostgreSQL 裡面產生樞紐分析表

樞紐分析表(Pivot Table)是什麼?在 Excel 裡面有提供這樣的東西,看似是被廣泛的使用的基本工具。

依照維基百科的說明,樞紐分析表是一種用來呈現資料性質總結(如計數,加總,平均或其他 Aggregation )的工具。

而一個樞紐表包含三個部份:

  1. 垂直欄位:這個欄位中,包含相異的品項。作為樞紐表的 Y 軸
  2. 水平欄位:和垂直欄位一樣,也包含相異的品項,但內容會挑跟垂直欄位不相同的項目來用。作為樞紐表的 X 軸
  3. X/Y 張開的表格之間:為要呈現的總結資訊。也就是,固定 (X, Y) 條件下,所得的 Aggregation 資訊(例如,固定 (X,Y) 條件後的資料筆數,或是固定 (X, Y) 條件下,其他欄位資料的平均值)
  4. (選擇性)對 X 軸 / Y 軸作小計


對應到關聯式表格的結構。樞紐表把兩欄用「品項」當成資料內容的關聯式表格欄位,挑出相異品項的清單,當作樞紐表的 X/Y 欄位陳列的「標籤」(例如,可以用 enum 型態來列舉資料內容的欄位,或是紀錄每日的日期/月份的欄位,都可以當成樞紐表 X/Y 軸的標籤);而中間的內容,就是對照 (X, Y) 值當成條件的 Aggregation 結果。

要從關聯式表格產生樞紐分析表,便要組織 SQL 查詢,呈現結果,再使用樞紐表組織工具處理

  • 準備作為 X 軸的欄位資料
  • 準備作為 Y 軸的欄位資料
  • 準備用來作總結的資料


以上的關係搞清楚之後,樞紐表能不能用 PostgreSQL 資料庫的 SQL 語法完成?

在 PostgreSQL 裡面提供了一個叫做 tablefun 的內建模組(PGSQL 8.3 之後引入),裡面有一個作樞紐分析表的工具 crosstab(),可以達成用設計過的 SQL 指令產生樞紐表的功能。
而在 9.6 版的客戶端指令界面 psql 下,有更容易使用的工具 \crosstabview,它針對前一次執行的 SELECT SQL 指令(需要是有設計過,呈現樞紐表結果的 SQL 指令,且不和 tablefun 模組的 crosstab() 函數混用!)結果以樞紐表的方式呈現。
  1. tablefun 模組的 crosstab(sql_text, sql_text) 函數,會吃兩個參數:第一個參數放的是組織過的 SELECT SQL 指令,第二個參數則是自行指定 X 軸品項的 SELECT SQL 指令(欄位裡面可能不見得每一品項都已經有資料,如月份資料還沒有存滿一年,這種狀況就能夠手動指定)。而最後回傳結果要當作 Set-Returning Record 來處理,把輸出的樞紐表當成普通表,指定各欄位(樞紐表 X 軸)的「名稱」
  2. psql 的 \crosstabview 可以接四個參數,前兩個參數是準備用來作樞紐表的水平/垂直欄位,第三個參數是樞紐的值,而第四個是選用的,用來指定水平欄位值排序的欄位。功能比 crosstab() 函數陽春一些,不果相對來說也比較簡單一點。
以下為一個 PGSQL 裡的關聯式表格與它的資料作為操作示範的範例(參考來源:EDB 範本資料
edb=# select deptno, job, sal 
 from emp  order by deptno;
 deptno |    job    |   sal   
--------+-----------+---------
     10 | CLERK     | 2031.25
     10 | MANAGER   | 3828.13
     10 | PRESIDENT | 7812.50
     20 | ANALYST   | 4687.50
     20 | MANAGER   | 4648.44
     20 | CLERK     | 1250.00
     20 | CLERK     | 1718.75
     20 | ANALYST   | 4687.50
     30 | SALESMAN  | 1953.13
     30 | SALESMAN  | 2343.75
     30 | SALESMAN  | 2500.00
     30 | MANAGER   | 4453.13
     30 | SALESMAN  | 1953.13
     30 | CLERK     | 1484.38
(14 rows)

把資料用格子呈現的話如下(弄到 Libreoffice Calc 裡面作後續示範)

deptno
job
sal
10
CLERK
2031.25
10
MANAGER
3828.13
10
PRESIDENT
7812.5
20
ANALYST
4687.5
20
MANAGER
4648.44
20
CLERK
1250
20
CLERK
1718.75
20
ANALYST
4687.5
30
SALESMAN
1953.13
30
SALESMAN
2343.75
30
SALESMAN
2500
30
MANAGER
4453.13
30
SALESMAN
1953.13
30
CLERK
1484.38

對上述表格建立樞紐表,通常有兩種:
(一) 總結樞紐表:此處範例示範的問題為,在部門編號 deptno 為 '10' 時,職稱 job 為 'Clerk' 的人有多少個?

用 Libreoffice Calc 的話,可以把上述 SQL 用 csv 匯出,然後用 Calc 開啟,就能夠拉樞紐表
使用步驟

1. 先把資料範圍選起來
2. 點 「資料 → 樞紐分析表 → 建立」


3. 把可用欄位 deptno 拖拉到「列的欄位」,job 拖拉到「欄的欄位」,再拉一次 job 到「資料欄位」,這時呈現「總和 - job」

4. 在「資料欄位」中「總和 - job」點兩下,換成計數,此時便會呈現「數計數 - job」


5. 得到結果!


以上是一般樞紐表操作步驟和結果。而在 PGSQL 的話,首先,先組織出所需資料的 SQL 指令
edb=# select deptno, job,
 count(job) AS "# of workers "
 from emp group by deptno, job
          order by deptno,job;
 deptno |    job    | # of workers  
--------+-----------+---------------
     10 | CLERK     |             1
     10 | MANAGER   |             1
     10 | PRESIDENT |             1
     20 | ANALYST   |             2
     20 | CLERK     |             2
     20 | MANAGER   |             1
     30 | CLERK     |             1
     30 | MANAGER   |             1
     30 | SALESMAN  |             4
(9 rows)

使用 tablefun 模組的 crosstab() 函數作樞紐表,比較困擾的是,必須事先得知 X 軸會有哪些品項才行
edb=# SELECT * FROM crosstab(
  $$select deptno, job,
    count(job) AS "# of workers" 
    from emp group by deptno, job
             order by deptno,job$$ , 
  $$select distinct job from emp
    order by job$$)
AS ("dept No." int, 
    "ANALYST" text, "CLERK" text, "MANAGER" text, 
    "PRESIDENT" text, "SALESMAN" text);
 dept No. | ANALYST | CLERK | MANAGER | PRESIDENT | SALESMAN 
----------+---------+-------+---------+-----------+----------
       10 |         | 1     | 1       | 1         | 
       20 | 2       | 2     | 1       |           | 
       30 |         | 1     | 1       |           | 4
(3 rows)

上面要注意的是,兩句 SQL 指令是類似 Dynamic SQL 一樣,當成字串,放到樞紐表函數內的。

使用 \crosstabview 作樞紐表,只要在 SQL 指令後面不加分號,直接接上 Meta-command 就好。先來試一下沒有指定 X 軸排序的狀況
edb=# select deptno, job,
 count(job) AS "# of workers "
 from emp group by deptno, job
          order by deptno,job
 \crosstabview deptno job 
 deptno | CLERK | MANAGER | PRESIDENT | ANALYST | SALESMAN 
--------+-------+---------+-----------+---------+----------
     10 |     1 |       1 |         1 |         |         
     20 |     2 |       1 |           |       2 |         
     30 |     1 |       1 |           |         |        4
(3 rows)

如果要指定 X 軸品項的排序,就得要使用 Window Function row_number() 多產生一個欄位才行
edb=# select deptno, job,
 count(job) AS "# of workers ", 
 row_number() over(order by job) AS ord
 from emp group by deptno, job
          order by deptno,job
 \crosstabview deptno job "# of workers " ord
 deptno | ANALYST | CLERK | MANAGER | PRESIDENT | SALESMAN 
--------+---------+-------+---------+-----------+----------
     10 |         |     1 |       1 |         1 |         
     20 |       2 |     2 |       1 |           |         
     30 |         |     1 |       1 |           |        4
(3 rows)



(二) 對 X/Y 軸標籤欄位之外的其他資料的作統計:在這裡用問題範例是,各個部門內同樣職銜的人的薪資的平均

先放一個 Libreoffice 下樞紐表的結果,步驟同上,只是現在換成拉 sal 到「資料欄位」,並選擇「平均 - sal」


呈現結果


接著一樣,要在 PGSQL 裡面處理的話,先確定查詢的 SELECT 指令
edb=# select deptno, job,
 avg(sal) AS "Average Salary"
 from emp group by deptno, job
          order by deptno,job;
 deptno |    job    |    Average Salary     
--------+-----------+-----------------------
     10 | CLERK     | 2031.2500000000000000
     10 | MANAGER   | 3828.1300000000000000
     10 | PRESIDENT | 7812.5000000000000000
     20 | ANALYST   | 4687.5000000000000000
     20 | CLERK     | 1484.3750000000000000
     20 | MANAGER   | 4648.4400000000000000
     30 | CLERK     | 1484.3800000000000000
     30 | MANAGER   | 4453.1300000000000000
     30 | SALESMAN  | 2187.5025000000000000
(9 rows)

使用 tablefun 模組的 crosstab() 函數作樞紐表,在這裡我要把第一格重新命名成 "Average Salary",這是不影響的
edb=# SELECT * FROM crosstab(
  $$select deptno, job,
    avg(sal) AS "Average Salary"
    from emp group by deptno, job
             order by deptno,job$$ , 
  $$select distinct job from emp
    order by job$$)
AS ("Average Salary" int, 
    "ANALYST" text, "CLERK" text, "MANAGER" text, 
    "PRESIDENT" text, "SALESMAN" text);
 Average Salary |        ANALYST        |         CLERK         |        MANAGER        |       PRESIDENT       |       SALESMAN        
----------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------
             10 |                       | 2031.2500000000000000 | 3828.1300000000000000 | 7812.5000000000000000 | 
             20 | 4687.5000000000000000 | 1484.3750000000000000 | 4648.4400000000000000 |                       | 
             30 |                       | 1484.3800000000000000 | 4453.1300000000000000 |                       | 2187.5025000000000000
(3 rows)

\crosstabview,除了把 Meta-Command 緊接著 SQL 之外,也可以在執行完 SQ㏒接著執行 Meta-Command。此外這個 Meta-Command 不能像 crosstab() 直接重新命名第一格
edb=# select deptno, job,
 avg(sal) AS "Average Salary", 
 row_number() over(order by job) AS ord
 from emp group by deptno, job
          order by deptno,job;
 deptno |    job    |    Average Salary     | ord 
--------+-----------+-----------------------+-----
     10 | CLERK     | 2031.2500000000000000 |   3
     10 | MANAGER   | 3828.1300000000000000 |   7
     10 | PRESIDENT | 7812.5000000000000000 |   8
     20 | ANALYST   | 4687.5000000000000000 |   1
     20 | CLERK     | 1484.3750000000000000 |   4
     20 | MANAGER   | 4648.4400000000000000 |   6
     30 | CLERK     | 1484.3800000000000000 |   2
     30 | MANAGER   | 4453.1300000000000000 |   5
     30 | SALESMAN  | 2187.5025000000000000 |   9
(9 rows)

edb=# \crosstabview deptno job "Average Salary" ord
 deptno |        ANALYST        |         CLERK         |        MANAGER        |       PRESIDENT       |       SALESMAN        
--------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------
     10 |                       | 2031.2500000000000000 | 3828.1300000000000000 | 7812.5000000000000000 |                      
     20 | 4687.5000000000000000 | 1484.3750000000000000 | 4648.4400000000000000 |                       |                      
     30 |                       | 1484.3800000000000000 | 4453.1300000000000000 |                       | 2187.5025000000000000
(3 rows)

以上便把 PGSQL 樞紐表功能做了一輪範例了。如果在 Postgres 要有像試算表軟體的樞紐表一樣,要多出 Sum Total 這樣的欄位小計,就需要自己多加幾條查詢條件,然後用 crosstab() 呈現(\crosstabview 則不行),詳細用法可以看參考資料的連結。

從上面的操作看起來,試算表軟體(像是 M$ Office Excel 或是 Libreoffice Calc)的拖拉操作,對於熟悉的人來說,比起寫 SQL 指令還容易多了。不過由於這類軟體也不見得能裝這麼大筆的資料。
不過用所謂的 BI 工具好像就有提供類似的樞紐分析表圖形界面操作,而且資料源可以是資料庫查詢的資料結果~例如 Knime(KNIME | Pivoting in Databases)或是 Pentaho(範例請看 Applying Pivot in Pentaho Kettle - Stack Overflow)等等

順帶一提,在 Postgres 裡面,會被使用來當樞紐表欄位且品項內容固定的表格欄位,可以考慮選用 enum 型態,比較容易管理資料內容,排除這些欄位中資料發生內容筆誤的可能。

在上面的測試過程中,發現比較新版本的 psql 程式,就算連線到舊版資料庫(在這裡用 9.6 版 psql 連線到 PGSQL 9.2 資料庫),這個 \crosstabview Meta-Command 也是可以用的!


最後,透過內建樞紐表功能可以作一個無聊的小事:用 SQL 和樞紐表功能寫九九乘法表~用這個當作結束
edb=# select a, b
           , a*b as "AxB"
      from generate_series(1,9) a
cross join generate_series(1,9) b;
 a | b | AxB 
---+---+-----
 1 | 1 |   1
 1 | 2 |   2
 1 | 3 |   3
 1 | 4 |   4
 1 | 5 |   5
 1 | 6 |   6
 1 | 7 |   7
 1 | 8 |   8
 1 | 9 |   9
 2 | 1 |   2
 2 | 2 |   4
 2 | 3 |   6
 2 | 4 |   8
 2 | 5 |  10
 2 | 6 |  12
 2 | 7 |  14
 2 | 8 |  16
 2 | 9 |  18
 3 | 1 |   3
 3 | 2 |   6
 3 | 3 |   9
 3 | 4 |  12
 3 | 5 |  15
 3 | 6 |  18
 3 | 7 |  21
 3 | 8 |  24
 3 | 9 |  27
 4 | 1 |   4
 4 | 2 |   8
 4 | 3 |  12
 4 | 4 |  16
 4 | 5 |  20
 4 | 6 |  24
 4 | 7 |  28
 4 | 8 |  32
 4 | 9 |  36
 5 | 1 |   5
 5 | 2 |  10
 5 | 3 |  15
 5 | 4 |  20
 5 | 5 |  25
 5 | 6 |  30
 5 | 7 |  35
 5 | 8 |  40
 5 | 9 |  45
 6 | 1 |   6
 6 | 2 |  12
 6 | 3 |  18
 6 | 4 |  24
 6 | 5 |  30
 6 | 6 |  36
 6 | 7 |  42
 6 | 8 |  48
 6 | 9 |  54
 7 | 1 |   7
 7 | 2 |  14
 7 | 3 |  21
 7 | 4 |  28
 7 | 5 |  35
 7 | 6 |  42
 7 | 7 |  49
 7 | 8 |  56
 7 | 9 |  63
 8 | 1 |   8
 8 | 2 |  16
 8 | 3 |  24
 8 | 4 |  32
 8 | 5 |  40
 8 | 6 |  48
 8 | 7 |  56
 8 | 8 |  64
 8 | 9 |  72
 9 | 1 |   9
 9 | 2 |  18
 9 | 3 |  27
 9 | 4 |  36
 9 | 5 |  45
 9 | 6 |  54
 9 | 7 |  63
 9 | 8 |  72
 9 | 9 |  81
(81 rows)
 
edb=# \crosstabview a b "AxB"
 a | 1 | 2  | 3  | 4  | 5  | 6  | 7  | 8  | 9  
---+---+----+----+----+----+----+----+----+----
 1 | 1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9
 2 | 2 |  4 |  6 |  8 | 10 | 12 | 14 | 16 | 18
 3 | 3 |  6 |  9 | 12 | 15 | 18 | 21 | 24 | 27
 4 | 4 |  8 | 12 | 16 | 20 | 24 | 28 | 32 | 36
 5 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45
 6 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54
 7 | 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63
 8 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72
 9 | 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81
(9 rows)


參考資料:







沒有留言:

張貼留言