当没有动态列时,将多列和多行的列连接成一个 varchar 值
Posted
技术标签:
【中文标题】当没有动态列时,将多列和多行的列连接成一个 varchar 值【英文标题】:Concatenate columns of multiple columns and multiple rows into one varchar value, when no of columns is dynamic 【发布时间】:2017-05-14 19:38:14 【问题描述】:所以我有这个动态查询,它返回一个具有动态列数的结果集,如下所示:
在这个结果集中,我们总是有 ID、FacilityName 和 cycleNum 列,但任务列的数量可以变化 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))
但我想不出任何方法来扩展它以使用动态列。所有列的列表都保存在主表中,因此如果需要,我们可以从那里获取以逗号分隔的列列表。
【问题讨论】:
【参考方案1】:这里是动态方式
与您的静态查询略有不同的方法。要使其动态化,请使用while
循环或CURSOR
生成Task1 + Task2 + ..TaskN
。然后在Select
查询中使用它。
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)
不用担心While Loop/CURSOR
的使用,因为我们没有在循环内执行任何资源密集型操作。
静态查询看起来像这样
;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)
【讨论】:
【参考方案2】:使用系统表。我更喜欢简短有效的解决方案:)。这是指南,我相信你可以用集中字符串自己完成它(如果你不确定,添加评论,我会添加整个代码或更多描述)。
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)
【讨论】:
以上是关于当没有动态列时,将多列和多行的列连接成一个 varchar 值的主要内容,如果未能解决你的问题,请参考以下文章