Sql server 当没有列是动态的时,将多列和多行的列连接到一个varchar值中

Sql server 当没有列是动态的时,将多列和多行的列连接到一个varchar值中,sql-server,sql-server-2008,tsql,group-concat,Sql Server,Sql Server 2008,Tsql,Group Concat,因此,我有一个动态查询,它返回一个具有动态列数的结果集,如下所示: 在此结果集中,ID、FacilityName和CycleEnum的列将始终存在,但任务列的数量可能会有所不同Task1、Task2、Task3。。。。。直到Taskn 我需要的最终结果集如下: 为此,我尝试了以下查询: Select distinct FacilityName, substring( ( Select ',' + a

因此,我有一个动态查询,它返回一个具有动态列数的结果集,如下所示:

在此结果集中,ID、FacilityName和CycleEnum的列将始终存在,但任务列的数量可能会有所不同Task1、Task2、Task3。。。。。直到Taskn

我需要的最终结果集如下:

为此,我尝试了以下查询:

        Select distinct FacilityName, 
          substring(
          (
            Select ',' + a   
            From (SELECT ID, FacilityName, 'Cycle-'+ cast(CycleNum as                        varchar)+'::' + 'Task1~' + cast(Task1 as varchar) + ',Task2~' + cast(Task2 as varchar) + ',Task3~' + cast(Task3 as varchar) + ';' as a FROM #tempTable) ST1
            Where ST1.FacilityName = ST2.FacilityName
            ORDER BY ST1.FacilityName
            For XML PATH ('')
        ), 2, 1000) CycleData
From (SELECT ID, FacilityName, 'Cycle-'+ cast(CycleNum as varchar)+'::' + 'Task1~' + cast(Task1 as varchar) + ',Task2~' + cast(Task2 as varchar) + ',Task3~' + cast(Task3 as varchar)+ ';' as a FROM #tempTable) ST2
这将适用于以下测试数据:

    create table #tempTable
    (
     ID int,
     FacilityName varchar(50),
     CycleNum int,
     Task1 datetime,
     Task2 datetime,
     Task3 datetime
    )

    Insert into #tempTable values 
    (1, 'A', 1, convert(varchar(10), getdate(), 126), convert(varchar(10),      dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()),  126)),
    (2, 'A', 2, convert(varchar(10), getdate(), 126), convert(varchar(10),    dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126)),
    (3, 'B', 1, convert(varchar(10), getdate(), 126), convert(varchar(10),   dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126)),
    (4, 'B', 2, convert(varchar(10), getdate(), 126), convert(varchar(10),   dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126))
但是我想不出任何方法可以扩展到使用动态列。所有列的列表都保存在主表中,因此如果需要,我们可以从主表中获取以逗号分隔的列列表。

这里是一种动态方法

与静态查询略有不同的方法。要使其动态化,请使用
while
循环或
光标
生成
Task1+Task2+…TaskN
。然后在
选择
查询中使用它

DECLARE @columns VARCHAR(50)='Task1,Task2,Task3', -- Pass the list of column names 
        @int     INT = 1,
        @sql     VARCHAR(8000)

SET @sql = '    ;WITH cte
         AS (SELECT *,
                    ''Cycle-'' + Cast(CycleNum AS VARCHAR(10)) + ''::'' '

WHILE @int <= Len(@columns) - Len(Replace(@columns, ',', '')) -- To find the number of Tasks in list
  BEGIN
      SET @sql += + '+''Task' + Cast(@int AS VARCHAR(10))
                  + '~''+' + 'Cast(Task'
                  + Cast(@int AS VARCHAR(10))
                  + ' AS VARCHAR(50)) + '','''
      SET @int += 1
  END

SET @sql += '   AS concat_dates
             FROM   #tempTable)
    SELECT DISTINCT FacilityName,
                    LEFT(CycleData, Len(CycleData) - 1)
    FROM   cte a
           CROSS apply(SELECT b.concat_dates + '',''
                       FROM   cte b
                       WHERE  a.FacilityName = b.FacilityName
                       FOR xml path('''')) cs (CycleData) 

                       '
--print @sql -- uncomment it to debug if you have any error when executing dynamic code
EXEC (@sql) 

使用系统表。我更喜欢简短有效的解决方案:)。这是一个指南,我相信您可以用concenting字符串来完成它(如果您不确定,请添加注释,我将添加整个代码或更多描述)

;WITH cte
     AS (SELECT *,
                'Cycle-' + Cast(CycleNum AS VARCHAR(10))
                + '::' + 'Task1~' + Cast(Task1 AS VARCHAR(50))
                + ',' + 'Task2~' + Cast(Task2 AS VARCHAR(50))
                + ',' AS concat_dates
         FROM   #tempTable)
SELECT DISTINCT FacilityName,
                LEFT(CycleData, Len(CycleData) - 1)
FROM   cte a
       CROSS apply(SELECT b.concat_dates + ','
                   FROM   cte b
                   WHERE  a.FacilityName = b.FacilityName
                   FOR xml path('')) cs (CycleData) 
declare @columns varchar(max)
SELECT @columns = isnull(@columns + '+',  '') + '''' + COLUMN_NAME + '~'' + Cast(' + COLUMN_NAME + ' as varchar(50))'
FROM tempdb.INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME like '#tempTable%'
    AND c.COLUMN_NAME LIKE 'Task%' -- filter columns you are interested in

declare @sql varchar(4000) = 'select *, ' + @columns + ' from #tempTable'
print @sql
exec (@sql)