有這樣一個需求,一個表單主表,一個擴展列表,查詢的時候要把擴展列表中的多行轉成主表多列。 比如 dt_zhubiao [主表] dt_kuozhanbiao [擴展表] 查詢時,會根據dt_zhubiao表的type來查詢,type欄位一樣時,dt_kuozhanbiao表條數和name都會一致,v ...
有這樣一個需求,一個表單主表,一個擴展列表,查詢的時候要把擴展列表中的多行轉成主表多列。
比如
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 。 記於此以作備份,好記性不如爛筆頭。 -:)