SQL SERVER 實現多行轉多列


有這樣一個需求,一個表單主表,一個擴展列表,查詢的時候要把擴展列表中的多行轉成主表多列。

比如

dt_zhubiao [主表]

id type title
1 1 表單1-1
2 1 表單1-2
3 2 表單2-1
4 2 表單2-2

dt_kuozhanbiao [擴展表]

id formid name title value
1 1 ext_a 工齡 18
2 1 ext_b 職稱 副級
3 2 ext_a 工齡 20
4 2 ext_b 職稱 正級
5 3 ext_2a 字段1 值1
6 3 ext_2b 字段2 值2
7 3 ext_2c 字段3 值3
8 4 ext_2a 字段1 值21
9 4 ext_2b 字段2 值22
10 4 ext_2c 字段3 值23

查詢時,會根據dt_zhubiao表的type來查詢,type字段一樣時,dt_kuozhanbiao表條數和name都會一致,value不一致。

想要的結果如下:

查詢type=1時,select * from dt_zhubiao where type = 1 ... 

id type title ext_a ext_b
1 1 表單1-1 18 副級
2 1 表單1-2 20 正級

查詢type=2時,select * from dt_zhubiao where type =2 ...

id type title ext_2a ext_2b ext_2c
3 2 表單2-1 值1 值2 值3
4 2 表單2-2 值21 值22 值23

 

那么問題來了,基於 select * from dt_zhubiao where type = ? 基礎sql語句,如何生成這種查詢結果 ? 

這個問題應該多用於動態表單,之前自己嘗試過join 、union去解決,都總差那么點意思。 -:)

后面去多個論壇發帖挨個問了個遍,終於尋到解決辦法了。SQL 代碼如下:

--測試數據
if not object_id(N'Tempdb..#主表') is null
    drop table #主表
Go
Create table #主表([id] int,[type] int,[title] nvarchar(25))
Insert #主表
select 1,1,N'表單1-1' union all
select 2,1,N'表單1-2' union all
select 3,2,N'表單2-1' union all
select 4,2,N'表單2-2'
GO
if not object_id(N'Tempdb..#擴展表') is null
    drop table #擴展表
Go
Create table #擴展表([id] int,[formid] int,[name] nvarchar(26),[title] nvarchar(23),[value] nvarchar(22))
Insert #擴展表
select 1,1,N'ext_a',N'工齡',N'18' union all
select 2,1,N'ext_b',N'職稱',N'副級' union all
select 3,2,N'ext_1',N'工齡',N'18' union all
select 4,2,N'ext_b',N'職稱',N'正級' union all
select 5,3,N'ext_2a',N'字段1',N'值1' union all
select 6,3,N'ext_2b',N'字段2',N'值2' union all
select 7,3,N'ext_2c',N'字段3',N'值3'union all
select 8,4,N'ext_2a',N'字段1',N'值1' union all
select 9,4,N'ext_2b',N'字段2',N'值2' union all
select 10,4,N'ext_2c',N'字段3',N'值3'
Go
--測試數據結束
DECLARE @sql VARCHAR(MAX)
SET @sql = 'select #主表.id,#主表.type,#主表.title'
SELECT  @sql = @sql + ',max(case name when ''' + name
        + ''' then [value] else null end)[' + name + ']'
FROM    ( Select DISTINCT name from #擴展表 JOIN #主表 ON  formid IN (SELECT id FROM #主表 WHERE type=2)
        ) a
SET @sql = @sql
    + ' from #擴展表 JOIN #主表 ON  formid =#主表.id  WHERE type=2 group by #主表.id,#主表.type,#主表.title'
EXEC(@sql) 

至此已經解決了我的問題,但是對於實際的項目運用還是缺少點什么,比如 分頁、條件篩選。那么得在此基礎上稍微修改一下,這個簡單我自己會做了  -:) 。

DECLARE @sql VARCHAR(MAX)
SET @sql = 'with tb as (select ROW_NUMBER() OVER(Order by  #主表.id ) AS rindex,#主表.id,#主表.type,#主表.title'
SELECT  @sql = @sql + ',max(case name when ''' + name
        + ''' then [value] else null end)[' + name + ']'
FROM    ( Select DISTINCT name from #擴展表 JOIN #主表 ON  formid IN (SELECT id FROM #主表 WHERE type=2)
        ) a
SET @sql = @sql
    + ' from #擴展表 JOIN #主表 ON  formid =#主表.id  WHERE type=2 group by #主表.id,#主表.type,#主表.title); select * from tb where ext_2b =''值1'' and rindex between  1 and 10 '  --拼接with 及查詢條件
EXEC(@sql) 

到這自我感覺應該差不多了,再改改做成個存儲過程應該可以用了,但是吧,想着用程序也去實現一遍,看看哪種實行起來更方便 -:) 

程序實現兩種思路,一種組裝table:先結合分頁、條件篩選等查出需要的主表數據集得到一個datatable,然后給datatable動態去添加對應列,然后循環去賦值(這種情況就無法實現針對擴展字段進行排序)。

DataTable tb = DbHelperSQL.Query("with tb as (select row_number over(order by #主表.id) as rindex,* from #主表 where id in (select formid from #擴展表 where value ='值2') adn type=2 );select * from tb where rindex between 1 and 10").Tables[0];
var ar = new System.Collections.ArrayList();
foreach (DataRow crow in DbHelperSQL.Query("select name from #擴展表 where formid in (select id from #主表 where type = 2)").Tables[0].Rows) {
    ar.Add(crow[0]);
    tb.Columns.Add(crow[0].ToString(), typeof(string));
}
//這里可以一次性加載tb數據集中包含的所有#擴展表數據,然后在內存中進行操作賦值,如下循環查詢數據庫賦值只是為了寫的方便
for (var i =0; i <tb.Rows.Count;i++) {
    var formid = tb.Rows[i]["id"];
    foreach (var ari in ar) {
        tb.Rows[i][ari.ToString()] = DbHelperSQL.GetSingle(string.Format("select value from #臨時表 where formid ={0} and name ='{1}'",formid,ari));
    }
 }
 return tb;

第二種就是組裝SQL語句啦,但是不在程序中組裝之前的@sql字符串,但是還是參考之前的SQL代碼思路,然后轉成程序代碼思路。在之前SQL實現的代碼中,最后的exec(@sql)前一行加上打印@sql的語句,就會得到最后執行的sql語句

select #主表.id,#主表.type,#主表.title
,max(case name when 'ext_2a' then [value] else null end)[ext_2a]
,max(case name when 'ext_2b' then [value] else null end)[ext_2b]
,max(case name when 'ext_2c' then [value] else null end)[ext_2c]
 from #擴展表 JOIN #主表 ON  formid =#主表.id  WHERE type=2 group by #主表.id,#主表.type,#主表.title

基於這個SQL語句用程序去拼接最終的SQL語句執行(包括分頁、條件篩選)。

string sql=" with tb as (select row_number over(order by #主表.id) rindex, #主表.id,#主表.type,#主表.title";
foreach (DataRow crow in DbHelperSQL.Query("select name from #擴展表 where formid in (select id from #主表 where type = 2) group by name").Tables[0].Rows) {
    sql +=",max(case name when '"+crow["name"]+"' then [value] else null end) "+crow["name"];
}
sql+=" from #擴展表 join #主表 on formid = #主表.id where type = 2 group by #主表.id,#主表.type,#主表.title); select * from tb where ext_2a ='值1' and rindex between 1 and 10 ";
return DbHelperSQL.Query(sql).Tables[0];
//此處單純的SQL語句拼接,也可以再次優化實現動態參數化。

這種情況是可以實現所有的列都能進行排序。 目前來講,還只是去實現這個功能,還沒有考慮性能優化之類的。

論壇原咨詢帖:https://bbs.csdn.net/topics/392999794 。 記於此以作備份,好記性不如爛筆頭。 -:)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM