数据库行转列的sql语句(zt)
轉載:http://www.cnblogs.com/Charles2008/archive/2008/03/04/1090162.html
問題描述
假設有張學生成績表(CJ)如下
Name Subject Result
張三 語文 80
張三 數學 90
張三 物理 85
李四 語文 85
李四 數學 92
李四 物理 82
現在 想寫 sql 語句???? 查詢后結果 為???
姓名 語文 數學 物理
張三 80 90 85
李四 85 92 82?????? 該怎么實現 ?
研究意義
??????? 這是個并不復雜的問題,但卻是數據庫中行轉列的一個典型例子,只要把這個抽象出來的具有普遍意義的問題研究透徹,其他類似的復雜問題迎刃而解。
問題分析
?????? 首先介紹下行轉列的概念,也許書上并沒有這個概念,行轉列說的是這樣一類問題:有時候為了數據庫表的設計滿足用戶的動態要求(比如添加字段),我們采用定義字段名表,然后定義一個字段值的表,這樣就達到了用靜態來表達動態,換句話說就是把數據庫表中本來應該是橫向的延伸轉化為縱向的延伸,再換句話說就是把數據庫表中本來應該是字段的增加轉化為記錄條數的增加。然而,在這樣設計下,固然靈活,確帶來了統計分析的麻煩,因為統計分析時,應該是以直觀的形式進行表現。換言之,統計分析時,我們又應該顯示為字段更多的那種。如果同時做到了數據存儲時列的增加轉化為行的增加,數據提取時又可得到列增加了的數據,數據庫表的這種設計就對用戶透明了。
??????? 本文前面提出的這個問題就是一個典型的在數據提取時要把以行增加形式的數據轉化為以列增加形式的數據。為什么這樣說呢?我們注意subject字段,subject里的內容在數據庫存儲時是以不同數據行的形式,換言之,是以行增加的形式,而輸出時,這里面的內容我們要變成字段名了。
??????? 衡量這個問題解決好壞我們有幾個標準:1.當數據正好就是上面這個樣子時,解決辦法能否得到正確的解;2.如果增加科目了科目的種類,解決方法是否仍然能行得通;3.如果有些人的某們課程的成績還沒有下來,換言之,數據庫中不是每個人每門課的成績都可以找到,數據庫缺少某個人某門課的成績的記錄。在這種情況下程序還能否得到合理的結果。
試驗環境
?????? 本試驗使用MS SQL Server 2005環境測試。
試驗過程
?????? 1.建立數據表,錄入數據
??????? CREATE TABLE [dbo].[CJ](
[name] [varchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[subject] [varchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[result] [int] NULL,
CONSTRAINT [PK_CJ] PRIMARY KEY CLUSTERED
(
[name] ASC,
[subject] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
???? 通過可視化界面或者用insert語句錄入數據
???? 2.第一個最直接,最簡單的做法
???????? select distinct c.[name] as 姓名,
(select result from CJ where [name] = c.[name] and subject = '語文' )as 語文,
(select result from CJ where [name] = c.[name] and subject = '數學' )as 數學,
(select result from CJ where [name] = c.[name] and subject = '物理' )as 物理
from CJ c
????????? 主要思想就是把任務分成兩步,第一步:把第一列生成出來。第二步:根據第一列每行的姓名取值,查詢該同學的各科成績join到第一步生成的只有一列表。distinct不能省略。
????????? 該方法能夠完成該任務,但只能滿足前文所述的評價標準1和標準3。當科目增多或者實際科目沒有這么多時統計的結果就不那么完美了。換言之,這種方法是靜態的,將科目在sql語句里寫死了。另外中間的幾個sql語句查詢效率似乎并不那么高,還需要掃描整個表,實際上應該只需要在一個學生對應的幾條記錄里找就可以了。
?????? 3.較好的辦法
????? 先不管標準2,想想能不能解決那個掃描的效率問題。于是得到了下面的辦法。
????? select?? [name] as 姓名,
sum(case when subject='語文' then result end) as 語文,
sum(case when subject='數學' then result end) as 數學,
sum(case when subject='物理' then result end) as 物理
from CJ group by [name]
????? 該辦法大致思想類似前一種。最大的改進是用了group by,由于用了group by后字段名除了group by的那個其他不能直接用,加了個集函數,實際上這個Sum只會加一項,因為這個表的主鍵是name + subject。用了group by就會解決掃描的效率問題,因為sum是計算的每個分組之類的。本方法的技巧之處在于case when的使用。
?????? 這個辦法還是不能滿足標準2。
????? 4.較完美的辦法
???? 現在就是怎么解決subject“由死到活”的問題。想到了一種辦法如下:
??? declare @s nvarchar(1000)
select @s = 'select [name] as 姓名'
select @s = @s + ',sum(case when subject=''' + cast(subject as varchar) + ''' then result end) as ' + subject from CJ group by subject
select @s = @s + ' from CJ group by [name]'
exec(@s)
???? 其實思想是基于前面那種辦法的,關鍵的地方就是通過動態生成sql語句,然后執行之。
??? 在@s的第一次累加中的代碼中一句from CJ group by subject很是有技巧性,可見簡單的select * from table t where .. 也是這么變化無窮,不得不佩服sql或者說關系型數據庫的智慧。
本人收獲
??? a.認真的分析一個簡單的問題的來龍去脈是很有意義的事情,浮躁的學風會讓你花費大量的時間結果一無所獲。
??? b.解決一個問題要有清晰的思路,在一時不知道完美的答案時,可試圖一步一步優化,向完美的方向靠近。
??? c.要善于分析問題的癥結所在,即抓住問題的本質。
寫到最后
??? 這個問題暫時就說到這里,之所以把文章寫出來是基于兩個目的,首先,作為學習心得,不敢獨享,希望更多的人能從中得到啟發。其次,簡單的問題也包含很多高深的知識,希望更多的高手能加入探討,分析本文的不當之處,并給出更好的辦法,或者提供更多的類似的例子,本文希望起到拋磚引玉的作用。
轉載于:https://www.cnblogs.com/51net/archive/2012/02/27/2390445.html
總結
以上是生活随笔為你收集整理的数据库行转列的sql语句(zt)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android的Menu状态动态设置方法
- 下一篇: [Linq]Linq To Xml (待