昨天在MSDN論壇看到一個帖子,帖子中LZ需要根據某列的值把其他列的值插入到額外列
建表腳本:
圖1
LZ說原表就是類似上面那樣,實際表中pay_lv_會有很多列至少100列,我這裏爲了測試只建了25個pay_lv_列
而LZ希望select出來的結果是下圖那樣
圖2
client列和pay_level列不變,增加一個pay_cost列
pay_cost列根據pay_level列的值去取pay_lv_列的值,或者我用下面的圖片會更加明白
圖3
例如第6行,pay_level的值是6,那麼就去pay_lv_6這一列的值(值是20)把他放到pay_cost列裏
其他也是一樣,第二行pay_level的值是10,那就去pay_lv_10這一列的值(值是17)把他放到pay_cost列裏
如此類推
要select出圖2的結果,有下面幾種方法
1、case when
2、UNPIVOT函數
3、遊標
我這裏再建另外一個表,這個表跟原表是一樣的,只是數據沒有那麼多,pay_lv_列數只有3列
(1)case when
1 SELECT client,[pay_level],( CASE pay_level 2 WHEN 1 THEN pay_lv_1 3 WHEN 2 THEN pay_lv_2 4 WHEN 3 THEN pay_lv_3 5 ELSE 0 6 END) AS 'pay_cost' 7 FROM #t;
圖4
(2)UNPIVOT函數
1 SELECT * INTO #tt 2 FROM ( SELECT * 3 FROM #t 4 ) p UNPIVOT 5 ( pay_cost FOR pay_lv IN ( pay_lv_1, pay_lv_2, pay_lv_3 ) )AS unpvt 6 WHERE CAST(RIGHT(pay_lv, 1) AS INT) = pay_level 7 8 SELECT [client],[pay_level],[pay_cost] FROM [#tt] 9 10 DROP TABLE [#tt]
圖5
上面兩個方法:CASE WHEN和UNPIVOT函數可以用拼接SQL的方法來做,不過由於本人功力不夠,寫不出來
(3)遊標
我不喜歡使用遊標,主要有兩個原因
1、每次用的時候,要打開筆記本看語法
2、佔用資源
我使用了下面的sql語句來解決LZ的問題
1 IF object_id('#ttt') IS NOT NULL 2 DROP TABLE #ttt 3 IF object_id('#temptb') IS NOT NULL 4 DROP TABLE #temptb 5 6 DECLARE @i INT 7 --用於循環的 8 SET @i = 1 9 DECLARE @pay_level INT 10 --保存pay_level字段的值 11 DECLARE @COUNT INT 12 --保存#t1表的總行數值 13 DECLARE @pay_lv INT 14 --用於保存pay_lv的值 15 DECLARE @sql NVARCHAR(2000) 16 17 CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT ) 18 19 SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1 20 21 22 --獲取#t1表的總行數 23 SELECT @COUNT = COUNT(*) FROM [#temptb] 24 WHILE @i <= @COUNT 25 BEGIN 26 SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i 27 --判斷列名是否存在,不存在就插入0 28 IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] ) 29 BEGIN 30 --用拼接sql的方法來獲得pay_lv列對應的值,然後插入到#ttt表 31 SET @sql = N'select ' + ' @pay_lv=pay_lv_' + CAST(@pay_level AS NVARCHAR(200)) + ' from #temptb where id=' + CAST(@i AS NVARCHAR(20)) 32 EXEC sp_executesql @sql, N'@pay_lv int output ', @pay_lv OUTPUT 33 INSERT INTO #ttt VALUES (@pay_lv) 34 END 35 ELSE 36 BEGIN 37 INSERT INTO #ttt VALUES(0) 38 END 39 SET @i = @i + 1 40 END 41 42 43 44 SELECT A.[client], A.[pay_level], B.[pay_cost] 45 FROM [#temptb] AS A 46 INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID] 47 ORDER BY A.[ID] ASC 48 49 DROP TABLE [#temptb] 50 DROP TABLE [#ttt]
我這個sql語句也需要拼接sql來達到LZ想要的效果
不過這篇文章的重點不是拼接SQL
重點是怎麼模仿遊標
其實這個方法是最原始的方法,之前解決論壇問題的時候用過,想不到這次也能用上
關鍵代碼有以下幾句
1 CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT ) 2 3 SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1 4 5 --獲取#t1表的總行數 6 SELECT @COUNT = COUNT(*) FROM [#temptb] 7 WHILE @i <= @COUNT 8 SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i 9 SET @i = @i + 1 10 ---------------------------------- 11 SELECT A.[client], A.[pay_level], B.[pay_cost] 12 FROM [#temptb] AS A 13 INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID] 14 ORDER BY A.[ID] ASC
原表是沒有自增id的,我建一個臨時表#temptb,臨時表有一個自增id,並把原表的數據全部放入臨時表
獲取臨時表的行數,用於循環
每次執行的時候根據 WHERE id = @i 來逐行逐行獲取值,變量@i每次循環都遞增1
將獲取到的值都插入到#ttt這個臨時表裏面,然後根據ID的值做兩表連接就可以得到LZ的結果
我說的無中生有就是“在原表裏增加一個自增id方便循環,既簡單又容易理解o(∩_∩)o ”
判斷
我這裏還用了一句
1 IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )
用於判斷要獲取值的pay_lv_列是否存在,如果存在就插入pay_lv_列的值,如果不存在就插入0
總結
其實如果覺得某樣東西很難去實現,能不能用一個變通的方法呢?多動腦筋,辦法會有的
如有不對的地方,歡迎大家拍磚o(∩_∩)o