SQL SERVER Implementing Multi-row to Multi-column

Keywords: SQL Server SQL Stored Procedure Database

There is such a requirement, a form main table, an extended list, query to expand the list of multi-row into the main table multi-column.

such as

dt_zhubiao [main table]

id type title
1 1 Form 1-1
2 1 Forms 1-2
3 2 Form 2-1
4 2 Form 2-2

dt_kuozhanbiao [Extended Table]

id formid name title value
1 1 ext_a Working years 18
2 1 ext_b Title Secondary level
3 2 ext_a Working years 20
4 2 ext_b Title Positive grade
5 3 ext_2a Field 1 Value 1
6 3 ext_2b Field 2 Value 2
7 3 ext_2c Field 3 Value 3
8 4 ext_2a Field 1 Value 21
9 4 ext_2b Field 2 Value 22
10 4 ext_2c Field 3 Value 23

When querying, it will query according to the type of dt_zhubiao table. When the type field is the same, the number of dt_kuozhanbiao table bars and the name will be the same, and the value is not the same.

The desired results are as follows:

When querying type=1, select * from dt_zhubiao where type=1...

id type title ext_a ext_b
1 1 Form 1-1 18 Secondary level
2 1 Forms 1-2 20 Positive grade

When querying type=2, select * from dt_zhubiao where type=2...

id type title ext_2a ext_2b ext_2c
3 2 Form 2-1 Value 1 Value 2 Value 3
4 2 Form 2-2 Value 21 Value 22 Value 23

 

So the question arises, based on select * from dt_zhubiao where type = basic sql statement, how to generate such query results?  

This problem should be mostly used in dynamic forms. I have tried join and union to solve this problem before, which is almost meaningful. -)

Later, I went to several forums to post one after another, and finally found a solution. The SQL code is as follows:

--test data
if not object_id(N'Tempdb..#Master table') is null
    drop table #Master table
Go
Create table #Master table([id] int,[type] int,[title] nvarchar(25))
Insert #Master table
select 1,1,N'Form 1-1' union all
select 2,1,N'Form 1-2' union all
select 3,2,N'Form 2-1' union all
select 4,2,N'Form 2-2'
GO
if not object_id(N'Tempdb..#Extended table') is null
    drop table #Extended table
Go
Create table #Extended table([id] int,[formid] int,[name] nvarchar(26),[title] nvarchar(23),[value] nvarchar(22))
Insert #Extended table
select 1,1,N'ext_a',N'Working years',N'18' union all
select 2,1,N'ext_b',N'Title',N'Secondary level' union all
select 3,2,N'ext_1',N'Working years',N'18' union all
select 4,2,N'ext_b',N'Title',N'Positive grade' union all
select 5,3,N'ext_2a',N'Field 1',N'Value 1' union all
select 6,3,N'ext_2b',N'Field 2',N'Value 2' union all
select 7,3,N'ext_2c',N'Field 3',N'Value 3'union all
select 8,4,N'ext_2a',N'Field 1',N'Value 1' union all
select 9,4,N'ext_2b',N'Field 2',N'Value 2' union all
select 10,4,N'ext_2c',N'Field 3',N'Value 3'
Go
--End of test data
DECLARE @sql VARCHAR(MAX)
SET @sql = 'select #Master table.id,#Master table.type,#Master table.title'
SELECT  @sql = @sql + ',max(case name when ''' + name
        + ''' then [value] else null end)[' + name + ']'
FROM    ( Select DISTINCT name from #Extended table JOIN #Master table ON  formid IN (SELECT id FROM #Master table WHERE type=2)
        ) a
SET @sql = @sql
    + ' from #Extended table JOIN #Master table ON  formid =#Master table.id  WHERE type=2 group by #Master table.id,#Master table.type,#Master table.title'
EXEC(@sql) 

So far, I have solved my problem, but there is still something missing for the actual project application, such as paging, conditional screening. So I have to modify it a little bit on this basis. I will do this simple myself.

DECLARE @sql VARCHAR(MAX)
SET @sql = 'with tb as (select ROW_NUMBER() OVER(Order by  #Master table.id ) AS rindex,#Master table.id,#Master table.type,#Master table.title'
SELECT  @sql = @sql + ',max(case name when ''' + name
        + ''' then [value] else null end)[' + name + ']'
FROM    ( Select DISTINCT name from #Extended table JOIN #Master table ON  formid IN (SELECT id FROM #Master table WHERE type=2)
        ) a
SET @sql = @sql
    + ' from #Extended table JOIN #Master table ON  formid =#Master table.id  WHERE type=2 group by #Master table.id,#Master table.type,#Master table.title); select * from tb where ext_2b =''Value 1'' and rindex between  1 and 10 '  --Splicing with And query conditions
EXEC(@sql) 

By this time, I think it's almost possible to change it into a stored procedure, but let's think about using the program to implement it again and see which one is more convenient to implement -:)

There are two ways to implement the program, one is to assemble tables: first, to find the data set of the main table needed by paging, conditional filtering, and so on, to get a datatable, then to add corresponding columns to the DataTable dynamically, and then to assign values circularly (in this case, it is impossible to sort the extended fields).

DataTable tb = DbHelperSQL.Query("with tb as (select row_number over(order by #Master table.id) as rindex,* from #Master table where id in (select formid from #Extended table where value ='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 #Extended table where formid in (select id from #Master table where type = 2)").Tables[0].Rows) {
    ar.Add(crow[0]);
    tb.Columns.Add(crow[0].ToString(), typeof(string));
}
//Here you can load it all at once. tb All contained in the data set#Expand table data and then assign it in memory. The following loops query database assignment just for writing convenience
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 #cursor where formid ={0} and name ='{1}'",formid,ari));
    }
 }
 return tb;

The second is to assemble the SQL statements, but not the @sql string before the program assembly, but still refer to the previous ideas of the SQL code, and then turn to the idea of the program code. In the previous SQL implementation code, the last exec(@sql) line plus the statement printing @sql will get the last SQL statement executed.

select #Master table.id,#Master table.type,#Main table.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 #Extended table JOIN #Master table ON  formid =#Master table.id  WHERE type=2 group by #Master table.id,#Master table.type,#Main table.title

Based on this SQL statement, the final SQL statement execution (including paging and conditional filtering) is spliced by program.

string sql=" with tb as (select row_number over(order by #Master table.id) rindex, #Master table.id,#Master table.type,#Master table.title";
foreach (DataRow crow in DbHelperSQL.Query("select name from #Extended table where formid in (select id from #Master table 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 #Extended table join #Master table on formid = #Master table.id where type = 2 group by #Master table.id,#Master table.type,#Master table.title); select * from tb where ext_2a ='Value 1' and rindex between 1 and 10 ";
return DbHelperSQL.Query(sql).Tables[0];
//Here it's simple. SQL Statement splicing can also be re-optimized to achieve dynamic parameterization.

In this case, all columns can be sorted. At present, it is only to achieve this function, without considering performance optimization and so on.

Forum original advisory posts: https://bbs.csdn.net/topics/392999794  . A good memory is better than a bad pen. -)

Posted by Mikersson on Fri, 09 Aug 2019 03:48:00 -0700