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