1. 程式人生 > >鋒利的SQL-SQL Server的表旋轉(行列轉換)

鋒利的SQL-SQL Server的表旋轉(行列轉換)

所謂表旋轉,就是將表的行轉換為列,或是將表的列轉換為行,這是從SQL Server 2005開始提供的新技術。因此,如果希望使用此功能,需要將資料庫的相容級別設定為90。表旋轉在某些方面也是解決了表的資料儲存和實際需要之間的矛盾。例如,圖9-4所示的是一個典型的產品銷售統計表,這種格式雖然便於閱讀,但是在進行資料表儲存的時候卻並不容易管理,產品銷售資料表通常需要設計成圖9-5所示的結構。這樣就帶來一個問題,使用者既希望資料容易管理,又希望能夠生成一種能夠容易閱讀的表格資料,這時候就可以使用表旋轉技術。

          

9-4 產品銷售表

9-5 資料表結構

9.4.1 PIVOT運算子

PIVOT

運算子用於將表的行轉換為列,並能同時對行執行聚合運算。其語法格式如下:

SELECT <非旋轉列>,

    [第一個旋轉列] AS <列名>,

    [第二個旋轉列] AS <列名>,

    ...

    [最後的旋轉列] AS <列名>

FROM

    (<SELECT 生成資料的查詢>) AS <為源查詢結果指定的別名>

PIVOT

(

    <聚合函式>(<被聚合的列>)

FOR

[<包含將被轉換為列標頭的值的列>]

    IN ( [第一個旋轉後的列], [第二個旋轉後的列],

    ... [最後一個旋轉後的列])

) AS <為旋轉表指定的別名>

<可選的 ORDER BY 子句>;

為了實現行的旋轉,源查詢獲得的結果應當具備三列,才能夠實現旋轉。第1列是不進行旋轉的列,屬於標誌列;第2列是屬性列,也稱為透視列,其中的值會被旋轉列名;第3列是屬性值列,這些值將作為新列的值。使用下面的語句建立一個示例表Orders,內容如表9-7所示。

CREATE TABLE Orders

(

   ProductID int NOT NULL,

   OrderDate datetime NOT NULL,

   ShipTo char(20) NOT NULL,

   SubTotal money NOT NULL

);

INSERT INTO Orders

VALUES (1,CAST('20090102' AS datetime), 'Shanghai', 100.00),

       (1, CAST('20090105' AS datetime), 'Shanghai',100.00),

       (1, CAST('20090123' AS datetime),'Jinan',    100.00),

       (2, CAST('20090125' AS datetime),'Shanghai', 100.00),

       (1, CAST('20090205' AS datetime),'Jinan',    100.00),

       (3, CAST('20090213' AS datetime),'Shanghai', 100.00),

       (3, CAST('20090219' AS datetime),'Shanghai', 100.00),

       (4, CAST('20090309' AS datetime),'Beijing',  100.00),

       (1, CAST('20090311' AS datetime),'Dalian',   100.00),

       (2, CAST('20090324' AS datetime),'Shanghai', 100.00),

       (3, CAST('20090326' AS datetime),'Wuhan',    100.00);

9-7                                                                     Orders表的內容

ProductID

OrderDate

ShipTo

SubTotal

1

2009-01-02 00:00:00.000

Shanghai           

100.00

1

2009-01-05 00:00:00.000

Shanghai           

100.00

1

2009-01-23 00:00:00.000

Jinan              

100.00

2

2009-01-25 00:00:00.000

Shanghai           

100.00

1

2009-02-05 00:00:00.000

Jinan              

100.00

3

2009-02-13 00:00:00.000

Shanghai           

100.00

3

2009-02-19 00:00:00.000

Shanghai           

100.00

4

2009-03-09 00:00:00.000

Beijing            

100.00

1

2009-03-11 00:00:00.000

Dalian             

100.00

2

2009-03-24 00:00:00.000

Shanghai           

100.00

3

2009-03-26 00:00:00.000

Wuhan              

100.00

Orders表中包含了3個月的產品銷售資料,現在假設要獲得像圖9-4所示的銷售表,則對源表的查詢首先需要獲得上面講的三列,參考下面的語句:

SELECT ProductID,

       MONTH(OrderDate) AS OrderMonth,

       SubTotal

FROM Orders;

查詢結果如表9-8所示。其中ProductID為標誌列,OrderMonth為屬性列,其中的月份要轉變為列的名稱,SubTotal為屬性值列,這些值將成為新列的值。

9-8                                                                   獲取到的三列內容

ProductID

OrderMonth

SubTotal

1

1

100.00

1

1

100.00

1

1

100.00

2

1

100.00

1

2

100.00

3

2

100.00

3

2

100.00

4

3

100.00

1

3

100.00

2

3

100.00

3

3

100.00

完整的旋轉查詢語句如下。查詢結果如表9-9所示。

SELECT ProductID,

       [1]AS Jan,

       [2]AS Feb,

       [3]AS Mar

FROM (SELECT ProductID, MONTH(OrderDate) ASOrderMonth, SubTotal

      FROMOrders) AS O1

PIVOT

(

  SUM(SubTotal)

   FOROrderMonth IN ([1], [2], [3])

) AS Pvt

ORDER BY ProductID;

9-9                                                                   旋轉後輸出的內容

ProductID

Jan

Feb

Mar

1

300.00

100.00

100.00

2

100.00

NULL

100.00

3

NULL

200.00

100.00

4

NULL

NULL

100.00

上面的查詢語句將按下面的步驟來獲取表9-9所示的結果集:

·   PIVOT首先按屬性值列之外的列(ProductIDOrderMonth)對輸入表Sales.Orders進行分組彙總,類似執行下面的語句,得到一個如表9-10所示的中間結果集。

SELECT ProductID,

       OrderMonth,

       SUM(SubTotal) AS SubTotal

FROM (SELECT ProductID,MONTH(OrderDate) AS OrderMonth, SubTotal

      FROM Orders) AS O1

GROUP BY ProductID,OrderMonth;

9-10                                                         Orders經分組彙總後的結果

ProductID

OrderMonth

SubTotal

1

1

300.00

1

2

100.00

1

3

100.00

2

1

100.00

2

3

100.00

3

2

200.00

3

3

100.00

4

3

100.00

·   PIVOT根據FOR OrderMonth IN指定的值123,首先在結果集中建立名為123的列,然後從表9-10所示的中間結果中取出SubTotal列中取出相符合的值,分別放置到123列中。此時得到的結果集的別名為pvt(見語句中AS pvt的指定)。結果集的內容如表9-11所示。

9-11                                   使用FOR OrderMonth IN ([1], [2], [3])後得到的結果集

ProductID

1

2

3

1

300.00

100.00

100.00

2

100.00

NULL

100.00

3

NULL

200.00

100.00

4

NULL

NULL

100.00

·   最後根據SELECT ProductID, [1] AS Jan,[2] AS Feb, [3] AS Mar FROM的指定,從別名pvt結果集中檢索資料,並分別將名為123的列在最終結果集中重新命名為JanFebMar,得到表9-9所示的結果集。這裡需要注意的是FROM的含義,其表示從經PIVOT關係運算符得到的pvt結果集中檢索資料,而不是從Orders或派生表O1中檢索資料。

SQL Server2005之前,要進行行列轉換比較煩瑣,你需要考慮源表中行與結果集中行的關係,屬性列中的每個唯一值在結果集中都需要一個列。像上面表9-7中的Orders表由於包含3個月份的資料,因此在SELECT列表中需要包含3個表示式,分別用於提取3個月份中的資料。下面語句的查詢結果與表9-9相同,請讀者自己分析下面的語句。

SELECT ProductID,

      SUM(CASE WHEN OrderMonth = 1 THEN SubTotal END) AS Jan,

      SUM(CASE WHEN OrderMonth = 2 THEN SubTotal END) AS Feb,

      SUM(CASE WHEN OrderMonth = 3 THEN SubTotal END) AS Mar

FROM (SELECT ProductID,

            MONTH(OrderDate) AS OrderMonth,

            SubTotal AS SubTotal

      FROMOrders) AS O1

GROUP BY ProductID;

9.4.2 UNPIVOT運算子

UNPIVOTPIVOT執行幾乎完全相反的操作,將列轉換為行。但是,UNPIVOT並不完全是PIVOT的逆操作,由於在執行PIVOT過程中,資料已經被進行了分組彙總,所以使用UNPIVOT並不會重現原始表值表示式的結果。假設表9-9所示的結果集儲存在一個名為MyPvt的表中,現在需要將列JanFebMar轉換到對應於相應產品ID的行值(即返回到表9-10所示的格式)。這意味著必須另外標識兩個列,一個用於儲存月份,一個用於儲存銷售額。為了便於理解,仍舊分別將這兩個列命名為OrderMonthSubTotal

下面的語句首先建立MyPvt表,然後將查詢資料插入到表中。

CREATE TABLE MyPvt

(

   ProductID int NOT NULL,

   Jan money,

   Feb money,

   Mar money

);

INSERT INTO MyPvt(ProductID, Jan, Feb, Mar)

SELECT ProductID,

       [1] AS Jan,

       [2] AS Feb,

       [3] AS Mar

FROM (SELECT ProductID,MONTH(OrderDate) AS OrderMonth, SubTotal

      FROM Orders) AS O1

PIVOT

(

   SUM(SubTotal)

   FOR OrderMonth IN ([1], [2], [3])

) AS Pvt

ORDER BY ProductID;

下面的語句執行UNPIVOT,將得到表9-12所示的查詢結果。

SELECT ProductID,OrderMonth, SubTotal

FROM MyPvt

UNPIVOT

(

   SubTotal FOR OrderMonth IN (Jan, Feb, Mar)

)AS UnPvt;

9-12                                                          UNPIVOT得到的查詢結果

ProductID

OrderMonth

SubTotal

1

Jan

300.00

1

Feb

100.00

1

Mar

100.00

2

Jan

100.00

2

Mar

100.00

3

Feb

200.00

3

Mar

100.00

4

Mar

100.00

上面的語句將按下面的步驟獲得輸出結果集:

·   首先建立一個臨時結果集的結構,該結構中包含MyPvt表中除IN (Jan, Feb, Mar)之外的列,以及SubTotalFOR OrderMonth中指定的屬性值列(SubTotal)和屬性列(OrderMonth)。

·   然後將在MyPvt中逐行檢索資料,將表的列名稱放入OrderMonth列中,將相應的值放入到SubTotal列中。

由於在PIVOT時為列指定了別名,所以在UNPIVOT後,OrderMonth列中的月份使用的是英文簡稱,而不是表9-10所示的格式。要得到表9-10所示的格式,可以在查詢語句中使用CASE表示式來解決這個問題,參考下面的語句:

SELECT ProductID,

      CAST(CASE

           WHEN OrderMonth ='Jan' THEN '1'

           WHEN OrderMonth ='Feb' THEN '2'

           WHEN OrderMonth ='Mar' THEN '3'

           END AS int) AS OrderMonth,

      SubTotal

FROM MyPvt

UNPIVOT

(

   SubTotalFOR OrderMonth IN (Jan, Feb, Mar)

)AS UnPvt;

SQL Server2005之前,則應當使用下面的語句:

SELECT * FROM

  (SELECTProductID,1 AS OrderMonth, Jan AS SubTotal

   FROMMyPvt

   UNION ALL

   SELECTProductID,2 AS OrderMonth, Feb

   FROMMyPvt

   UNION ALL

   SELECT ProductID,3 AS OrderMonth, Mar

   FROMMyPvt) AS O

WHERE SubTotal IS NOT NULL;

9.4.3 CASE表示式9.4節我們介紹了使用PIVOT運算子進行錶行列轉換的方法,有效解決了表的資料儲存和方便閱讀的矛盾問題,本節將提供另外的一種轉換方式。這是在做一個考試系統時遇到的問題,下面是是建立示例表的語句。表中存放著學生每次各科的考試成績。

CREATE TABLE exams

(stu_name char(10) NOT NULL,

 exam_datedate NOT NULL,

 exam_subchar(10) NOT NULL,

 exam_scoreint);

INSERT INTO exams

VALUES ('張三', '2009-06-20', '語文', 90),

       ('張三', '2009-06-20', '數學', 95),

       ('張三', '2009-06-20', '英語', 100),

       ('張三', '2009-09-20', '語文', 85),

       ('張三', '2009-09-20', '數學', 90),

       ('張三', '2009-09-20', '英語', 98),

       ('李四', '2009-06-20', '語文', 80),

       ('李四', '2009-06-20', '數學', 85),

       ('李四', '2009-06-20', '英語', 90),

       ('李四', '2009-09-20', '語文', 75),

       ('李四', '2009-09-20', '數學', 80),

       ('李四', '2009-09-20', '英語', 88);

現在要獲得如表12-23所示的格式,每個學生按考試日期在表中佔一行。

12-23                                                                     轉換後結果

stu_name

exam_date

語文

數學

英語

張三

2009-06-20

90

95

100

張三

2009-09-20

85

90

98

李四

2009-06-20

80

85

90

李四

2009-09-20

75

80

88

在許多情況下,都可以使用CASE表示式將表的行轉換為列,這是一個非常有用的技巧。參考下面的語句:

SELECT stu_name, exam_date,

       CASEWHEN exam_sub = '語文' THENexam_score

            ELSE NULL

       ENDAS 語文,

       CASEWHEN exam_sub = '數學' THENexam_score

           ELSE NULL

       ENDAS 數學,

       CASEWHEN exam_sub = '英語' THENexam_score

           ELSE NULL

       ENDAS 英語

FROM exams;      

上面語句將得到如表12-24所示的結果。

12-24                                                     使用CASE表示式得到的結果

stu_name

exam_date

語文

數學

英語

張三

2009-06-20

90

NULL

NULL

張三

2009-06-20

NULL

95

NULL

張三

2009-06-20

NULL

NULL

100

張三

2009-09-20

85

NULL

NULL

張三

2009-09-20

NULL

90

NULL

張三

2009-09-20

NULL

NULL

98

李四

2009-06-20

80

NULL

NULL

李四

2009-06-20

NULL

85

NULL

李四

2009-06-20

NULL

NULL

90

李四

2009-09-20

75

NULL

NULL

李四

2009-09-20

NULL

80

NULL

李四

2009-09-20

NULL

NULL

88

由上表可以看出,只要按stu_nameexam_date分組計算最大值,就可以得到表12-23所要求的計算結果。參考下面的語句:

SELECT stu_name, exam_date,

      MAX(CASE WHEN exam_sub = '語文' THEN exam_score

               ELSE NULL

          END) AS 語文,

      MAX(CASE WHEN exam_sub = '數學' THEN exam_score

               ELSE NULL

          END) AS 數學,

      MAX(CASE WHEN exam_sub = '英語' THEN exam_score

               ELSE NULL

           END) AS 英語

FROM exams

GROUP BY stu_name, exam_date

ORDER BY stu_name, exam_date;

相關推薦

鋒利SQL-SQL Server旋轉行列轉換

所謂表旋轉,就是將表的行轉換為列,或是將表的列轉換為行,這是從SQL Server 2005開始提供的新技術。因此,如果希望使用此功能,需要將資料庫的相容級別設定為90。表旋轉在某些方面也是解決了表的資料儲存和實際需要之間的矛盾。例如,圖9-4所示的是一個典型的產品銷售統計表

PostgreSQL 實現交叉行列轉換的五種方法

這裡我來演示下在POSTGRESQL裡面如何實現交叉表的展示,至於什麼是交叉表,我就不多說了,度娘去哦。原始表資料如下: t_girl=# select * from score;    name  | subject | score    -------+------

內連線、外連線、子查詢(exists用法,關聯/非關聯子查詢)、課堂練習行列轉換、rownum和rowid

笛卡爾積 和內連線 外連線 實際上是兩張表的乘積,查詢結果沒有實際意義 select * from emp,dept; 內連線-等值內連線(隱式) select * from emp,dept where emp.deptno = dept.deptno

SQL Server 更改跟蹤Chang Tracking監控資料

一.本文所涉及的內容(Contents) 二.背景(Contexts)   在SQL Server 2008以上版本中,對資料庫中的使用者表所做的 DML 更改(插入、更新和刪除操作)除了:SQL Server 變更資料捕獲(CDC)監控表資料之外,還有一個新增功能,那就是:更改跟蹤(Chang Tr

Linq to SQL 的連查詢

equal query 交集 數據庫 調用 數據 變量 bst log 關於數據庫的查詢中經常需要用到多表的連接查詢,這裏就簡單地展示關於linq的查詢功能。 1、單表的查詢 [csharp] view plain copy var query = from

2018年11月16日SQL Server實驗內容觸發器實驗

--注意:先把studentmanager資料庫中的所有表用select into命令複製一份, --然後用複製後新表完成下面的實驗,同時,對每個觸發器都要進行驗證。 select *into department2 from department select *into course2 fro

Sql Server資料庫解決單個使用者打開不了資料庫的問題

今天準備copy sql server資料庫中的.mdf檔案,但發現copy不了,於是上網搜了一下發現要分離資料庫,但是分離了一段時間系統又顯示出錯,然後分離的資料庫後面就多了單個使用者四個字,資料庫就訪問不了了。最後開啟新建查詢,執行以下指令碼便順利解決: U

經典SQL Server語句大全超詳細

經典SQL語句大全 一、基礎 1、說明:建立資料庫 CREATE DATABASE database-name 2、說明:刪除資料庫 drop database dbname 3、說明:備份sql server --- 建立 備份資料的 device

SQL Server 監控系列文章索引

一.前言(Introduction)   SQL Server監控在很多時候可以幫助我們瞭解資料庫做了些什麼,比如誰誰在什麼時候修改了表結構,誰誰在刪除了某個物件,當這些事情發生了,老闆在後面追著說這是誰幹的,如果你找不出元凶,那麼就成為背黑鍋的人了。   如果你想更瞭解什麼時候需要對資料庫做什麼監控,那

SQL Server 複製系列文章索引

2013-09-30 17:04 by 聽風吹雨, ... 閱讀, ... 評論, 收藏, 編輯 一.本文所涉及的內容(Contents) 二.前言(Introduction)    SQL Server的複製、日誌傳送、映象等幾個

SQL Server 事件通知Event notifications

一.本文所涉及的內容(Contents) 二.背景(Contexts)   SQL Server事件通知有什麼用呢?如果你想監控SQL Server的DDL操作,你可以通過DDL觸發器(參考:SQL Server DDL觸發器運用),也可以通過SQL Server 事件通知把這個事件相關的資訊傳送到

SQL Server 預設跟蹤Default Trace

一.本文所涉及的內容(Contents) 二.背景(Contexts)   思考這樣的場景:資料庫的表、儲存過程經常別修改,當這些修改造成BUG的時候,很多開發都不承認是他們乾的,那我們有沒辦法找出誰幹的呢?   SQL Server有Default Trace預設跟蹤,資料庫記錄資訊到log.tr

部署SQL Server Analysis Service 分析服務: 帳戶名稱與安全性識別碼無法對應

     之前建立的SQL Server的分析服務的工程,未發生任何異常。但是最近對其中的內容做了一些修改,再次部署的時候,總是提示:賬戶名稱與安全性識別碼無法對應。      首先以為是賬號許可權不足,檢查之後發現沒有問題。      接下來以為是啟動Analysis S

SQL Server進階十五常用函式

在SQL 2012基礎教程中列出子句是按照以下順序進行邏輯處理。 FROM WHERE GROUP BY HAVING SELECT ORDER BY   IF EXISTS (SELECT 1 FROM Ta

SQL Server進階十五常用函數

mon har 標量 reat 類型 rom 入參 bold 常用函數 在SQL 2012基礎教程中列出子句是按照以下順序進行邏輯處理。 FROM WHERE GROUP BY HAVING SELECT ORDER BY

啟動SQL Server Agent服務開機啟動

在cmd命令提示符下,輸入下列命令之一: net start "SQL Server Agent (MSSQLSERVER)"  (這個我試過無法使用) - 或 - net start SQLSERVERAGENT  ( 我是用這個啟動的) 方法二、 這個也可以在電腦中手動啟動,win7下

【菜鳥系列】SQL Server跨伺服器跨例項訪問資料庫

/*** 竟然又一次來寫部落格了~是經理建議我堅持寫部落格的~看了3個月前我的第一篇部落格,也覺得很有必要 囉嗦幾句~ 一眨眼3個月過去了~本人有沒有變得NB一點了呢? 完全沒有!!!而且還變的更菜了~現在看自己寫的部落格已經有點看不懂了~真是太不幸了~ 最近單位做了一款

一道SQL面試題行列互換

有一個SQL題在面試中出現的概率極高,最近有學生出去面試仍然會遇到這樣的題目,在這裡跟大家分享一下。 題目:資料庫中有一張如下所示的表,表名為sales。 年 季度 銷售量

SQL實現小算法輔助決策_ 計算商品評分、及時補貨

mysql分別把 計算各自的 1、點擊量/點擊量均值 2、銷售量/銷售量均值 兩者相加,可以得到一個簡單評分 又有問題了,豬肉的評分不應該比五花肉多。 因此我們要加入簡單的權重,譬如點擊量評分占30%。銷售量評分占70%select p_type,p_name, (p_view/view_avg)

深入研究Spark SQL的Catalyst優化器原創翻譯

超越 href 語法 英文 更多 com edi 此外 並行化 Spark SQL是Spark最新和技術最為復雜的組件之一。它支持SQL查詢和新的DataFrame API。Spark SQL的核心是Catalyst優化器,它以一種新穎的方式利用高級編程語言特性(例如Sca