一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - 數據庫技術 - SQL 中的行轉列和列轉行

SQL 中的行轉列和列轉行

2021-11-09 20:58SQL數據庫開發Lyven 數據庫技術

行轉列,列轉行是我們在開發過程中經常碰到的問題。行轉列一般通過CASE WHEN 語句來實現,也可以通過 SQL SERVER 的運算符PIVOT來實現。

行轉列,列轉行是我們在開發過程中經常碰到的問題。行轉列一般通過CASE WHEN 語句來實現,也可以通過 SQL SERVER 的運算符PIVOT來實現。用傳統的方法,比較好理解。層次清晰,而且比較習慣。 但是PIVOT 、UNPIVOT提供的語法比一系列復雜的SELECT…CASE 語句中所指定的語法更簡單、更具可讀性。下面我們通過幾個簡單的例子來介紹一下列轉行、行轉列問題。

SQL 中的行轉列和列轉行

我們首先先通過一個老生常談的例子,學生成績表(下面簡化了些)來形象了解下行轉列

  1. CREATETABLE[StudentScores]
  2. (
  3. [UserName]NVARCHAR(20),--學生姓名
  4. [Subject]NVARCHAR(30),--科目
  5. [Score]FLOAT,--成績
  6. )
  7. INSERTINTO[StudentScores]SELECT'Nick','語文',80
  8. INSERTINTO[StudentScores]SELECT'Nick','數學',90
  9. INSERTINTO[StudentScores]SELECT'Nick','英語',70
  10. INSERTINTO[StudentScores]SELECT'Nick','生物',85
  11. INSERTINTO[StudentScores]SELECT'Kent','語文',80
  12. INSERTINTO[StudentScores]SELECT'Kent','數學',90
  13. INSERTINTO[StudentScores]SELECT'Kent','英語',70
  14. INSERTINTO[StudentScores]SELECT'Kent','生物',85

如果我想知道每位學生的每科成績,而且每個學生的全部成績排成一行,這樣方便我查看、統計,導出數據

  1. SELECT
  2. UserName,
  3. MAX(CASESubjectWHEN'語文'THENScoreELSE0END)AS'語文',
  4. MAX(CASESubjectWHEN'數學'THENScoreELSE0END)AS'數學',
  5. MAX(CASESubjectWHEN'英語'THENScoreELSE0END)AS'英語',
  6. MAX(CASESubjectWHEN'生物'THENScoreELSE0END)AS'生物'
  7. FROMdbo.[StudentScores]
  8. GROUPBYUserName

查詢結果如圖所示,這樣我們就能很清楚的了解每位學生所有的成績了

SQL 中的行轉列和列轉行

接下來我們來看看第二個小列子。有一個游戲玩家充值表(僅僅為了說明,舉的一個小例子),

  1. CREATETABLE[Inpours]
  2. (
  3. [ID]INTIDENTITY(1,1),
  4. [UserName]NVARCHAR(20),--游戲玩家
  5. [CreateTime]DATETIME,--充值時間
  6. [PayType]NVARCHAR(20),--充值類型
  7. [Money]DECIMAL,--充值金額
  8. [IsSuccess]BIT,--是否成功1表示成功,0表示失敗
  9. CONSTRAINT[PK_Inpours_ID]PRIMARYKEY(ID)
  10. )
  11. INSERTINTOInpoursSELECT'張三','2010-05-01','支付寶',50,1
  12. INSERTINTOInpoursSELECT'張三','2010-06-14','支付寶',50,1
  13. INSERTINTOInpoursSELECT'張三','2010-06-14','手機短信',100,1
  14. INSERTINTOInpoursSELECT'李四','2010-06-14','手機短信',100,1
  15. INSERTINTOInpoursSELECT'李四','2010-07-14','支付寶',100,1
  16. INSERTINTOInpoursSELECT'王五','2010-07-14','工商銀行卡',100,1
  17. INSERTINTOInpoursSELECT'趙六','2010-07-14','建設銀行卡',100,1

下面來了一個統計數據的需求,要求按日期、支付方式來統計充值金額信息。這也是一個典型的行轉列的例子。我們可以通過下面的腳本來達到目的

  1. SELECT
  2. CONVERT(VARCHAR(10),CreateTime,120)ASCreateTime,
  3. CASEPayTypeWHEN'支付寶'THENSUM(Money)ELSE0ENDAS'支付寶',
  4. CASEPayTypeWHEN'手機短信'THENSUM(Money)ELSE0ENDAS'手機短信',
  5. CASEPayTypeWHEN'工商銀行卡'THENSUM(Money)ELSE0ENDAS'工商銀行卡',
  6. CASEPayTypeWHEN'建設銀行卡'THENSUM(Money)ELSE0ENDAS'建設銀行卡'
  7. FROMInpours
  8. GROUPBYCreateTime,PayType

如圖所示,我們這樣只是得到了這樣的輸出結果,還需進一步處理,才能得到想要的結果

SQL 中的行轉列和列轉行

  1. SELECT
  2. CreateTime,
  3. ISNULL(SUM([支付寶]),0)AS[支付寶],
  4. ISNULL(SUM([手機短信]),0)AS[手機短信],
  5. ISNULL(SUM([工商銀行卡]),0)AS[工商銀行卡],
  6. ISNULL(SUM([建設銀行卡]),0)AS[建設銀行卡]
  7. FROM
  8. (
  9. SELECT
  10. CONVERT(VARCHAR(10),CreateTime,120)ASCreateTime,
  11. CASEPayTypeWHEN'支付寶'THENSUM(Money)ELSE0ENDAS'支付寶',
  12. CASEPayTypeWHEN'手機短信'THENSUM(Money)ELSE0ENDAS'手機短信',
  13. CASEPayTypeWHEN'工商銀行卡'THENSUM(Money)ELSE0ENDAS'工商銀行卡',
  14. CASEPayTypeWHEN'建設銀行卡'THENSUM(Money)ELSE0ENDAS'建設銀行卡'
  15. FROMInpours
  16. GROUPBYCreateTime,PayType
  17. )T
  18. GROUPBYCreateTime

其實行轉列,關鍵是要理清邏輯,而且對分組(Group by)概念比較清晰。上面兩個列子基本上就是行轉列的類型了。但是有個問題來了,上面是我為了說明弄的一個簡單列子。實際中,可能支付方式特別多,而且邏輯也復雜很多,可能涉及匯率、手續費等等(曾經做個這樣一個),如果支付方式特別多,我們的CASE WHEN 會弄出一大堆,確實比較惱火,而且新增一種支付方式,我們還得修改腳本如果把上面的腳本用動態SQL改寫一下,我們就能輕松解決這個問題

  1. DECLARE@cmdTextVARCHAR(8000);
  2. DECLARE@tmpSqlVARCHAR(8000);
  3. SET@cmdText='SELECTCONVERT(VARCHAR(10),CreateTime,120)ASCreateTime,'+CHAR(10);
  4. SELECT@cmdText=@cmdText+'CASEPayTypeWHEN'''+
  5. PayType+'''THENSUM(Money)ELSE0ENDAS'''+PayType
  6. +''','+CHAR(10)FROM(SELECTDISTINCTPayTypeFROMInpours)T
  7. SET@cmdText=LEFT(@cmdText,LEN(@cmdText)-2)--注意這里,如果沒有加CHAR(10)則用LEFT(@cmdText,LEN(@cmdText)-1)
  8. SET@cmdText=@cmdText+'FROMInpours
  9. GROUPBYCreateTime,PayType';
  10. SET@tmpSql='SELECTCreateTime,'+CHAR(10);
  11. SELECT@tmpSql=@tmpSql+'ISNULL(SUM('+PayType+'),0)AS'''+
  12. PayType+''','+CHAR(10)
  13. FROM(SELECTDISTINCTPayTypeFROMInpours)T
  14. SET@tmpSql=LEFT(@tmpSql,LEN(@tmpSql)-2)+'FROM('+CHAR(10);
  15. SET@cmdText=@tmpSql+@cmdText+')TGROUPBYCreateTime';
  16. PRINT@cmdText
  17. EXECUTE(@cmdText);

下面是通過PIVOT來進行行轉列的用法,大家可以對比一下,確實要簡單、更具可讀性

  1. SELECTCreateTime,[支付寶],[手機短信],[工商銀行卡],[建設銀行卡]
  2. FROM
  3. (
  4. SELECTCONVERT(VARCHAR(10),CreateTime,120)ASCreateTime,PayType,Money
  5. FROMInpours
  6. )P
  7. PIVOT(
  8. SUM(Money)
  9. FORPayTypeIN
  10. ([支付寶],[手機短信],[工商銀行卡],[建設銀行卡])
  11. )AST
  12. ORDERBYCreateTime

有時可能會出現這樣的錯誤:

消息 325,級別 15,狀態 1,第 9 行

‘PIVOT’ 附近有語法錯誤。您可能需要將當前數據庫的兼容級別設置為更高的值,以啟用此功能。有關存儲過程 sp_dbcmptlevel 的信息,請參見幫助。

這個是因為:對升級到 SQL Server 2005 或更高版本的數據庫使用 PIVOT 和 UNPIVOT 時,必須將數據庫的兼容級別設置為 90 或更高。有關如何設置數據庫兼容級別的信息,請參閱 sp_dbcmptlevel (Transact-SQL)。 例如,只需在執行上面腳本前加上 EXEC sp_dbcmptlevel Test, 90; 就OK了, Test 是所在數據庫的名稱。

下面我們來看看列轉行,主要是通過UNION ALL ,MAX來實現。假如有下面這么一個表

  1. CreateTableProgrectDetail
  2. (
  3. ProgrectNameNVARCHAR(20),--工程名稱
  4. OverseaSupplyINT,--海外供應商供給數量
  5. NativeSupplyINT,--國內供應商供給數量
  6. SouthSupplyINT,--南方供應商供給數量
  7. NorthSupplyINT--北方供應商供給數量
  8. )
  9. INSERTINTOProgrectDetail
  10. SELECT'A',100,200,50,50
  11. UNIONALL
  12. SELECT'B',200,300,150,150
  13. UNIONALL
  14. SELECT'C',159,400,20,320
  15. UNIONALL
  16. SELECT'D',250,30,15,15

我們可以通過下面的腳本來實現,查詢結果如下圖所示

  1. SELECTProgrectName,'OverseaSupply'ASSupplier,
  2. MAX(OverseaSupply)AS'SupplyNum'
  3. FROMProgrectDetail
  4. GROUPBYProgrectName
  5. UNIONALL
  6. SELECTProgrectName,'NativeSupply'ASSupplier,
  7. MAX(NativeSupply)AS'SupplyNum'
  8. FROMProgrectDetail
  9. GROUPBYProgrectName
  10. UNIONALL
  11. SELECTProgrectName,'SouthSupply'ASSupplier,
  12. MAX(SouthSupply)AS'SupplyNum'
  13. FROMProgrectDetail
  14. GROUPBYProgrectName
  15. UNIONALL
  16. SELECTProgrectName,'NorthSupply'ASSupplier,
  17. MAX(NorthSupply)AS'SupplyNum'
  18. FROMProgrectDetail
  19. GROUPBYProgrectName

SQL 中的行轉列和列轉行

用UNPIVOT 實現如下:

  1. SELECTProgrectName,Supplier,SupplyNum
  2. FROM
  3. (
  4. SELECTProgrectName,OverseaSupply,NativeSupply,
  5. SouthSupply,NorthSupply
  6. FROMProgrectDetail
  7. )T
  8. UNPIVOT
  9. (
  10. SupplyNumFORSupplierIN
  11. (OverseaSupply,NativeSupply,SouthSupply,NorthSupply)
  12. )P

原文鏈接:https://mp.weixin.qq.com/s?__biz=MzA3MTg4NjY4Mw==&mid=2457323770&idx=2&sn=d3209bed52a39106bbe452a9a5b35b3c&chksm=88a5dacebfd253d8acaf8278b10e3b8331ed12b4fcb41eaab6a6d05cae82869ad56111e1a6b2&mpshare=1&

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产高清一区二区三区免费视频 | 四虎免费在线视频 | kk4kk免费毛片| 欧美精品国产一区二区 | 天天做天天爰夜夜爽 | 操弄哥哥的108种姿势 | 成人影院在线看 | 全黄h全肉细节文在线观看 全彩成人18h漫画 | 高清不卡一区二区 | 亚洲国产精品久久丫 | 调教小龙女 | 大香人蕉免费视频75 | 亚洲欧美久久婷婷爱综合一区天堂 | 秋霞啪啪片| 四虎黄色影视 | 性姿势女人嗷嗷叫图片 | 亚洲天堂伦理 | 亚洲精品一二区 | 成人综合婷婷国产精品久久免费 | 国产午夜久久精品 | 色里番52kkm全彩 | 古装一级无遮挡毛片免费观看 | 日本无遮挡亲吻膜下面免费 | 草草影院永久在线观看 | 高h禁伦奶水女 | 免费高清视频在线观看 | 国产成人手机在线 | www亚洲国产 | 国产nv精品你懂得 | 四虎成人网 | 国产日韩精品一区二区 | 99久久香蕉国产线看观香 | 成人区精品一区二区毛片不卡 | 国产成人精品一区二区 | 婷婷丁香视频 | 国产精品久久久久久影院 | 国产精品自拍一区 | 色一情| 99国产牛牛视频在线网站 | 外国黄色软件 | 日韩无砖专区体验区 |