SQL 将 CTE 生成的行转换为列 - 包括字段名
SQL convert rows generated by CTE to columns - including fieldnames
我有一个 CTE,可以将数据生成可变行数。这些是我需要在连接到另一个 table 时使用的小时值,但这部分在这里并不重要。
单独的 CTE 部分如下所示:
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + @interval AS startpos
FROM cteHour
WHERE (startpos + @interval) < @period
) SELECT fieldname, startpos FROM cteHour
它生成这样的数据(取决于 @interval
和 @period
值):
fieldname startpos
--------- -----------
i0 0
i1 3
i2 6
i3 9
i4 12
i5 15
i6 18
i7 21
我的问题是如何使用第一列作为字段名称将结果放入列而不是行中,如下所示:
i0 i1 i2 i3 i4 i5 i6 i7
-- -- -- -- -- -- -- --
0 3 6 9 12 15 18 21
我猜它需要 PIVOT
,但据我所知,旋转不能动态生成字段名称。所以我愿意接受任何能完成工作的答案。
因为它需要动态 sql,请跳过数据透视部分并直接从您的 cte
生成动态查询
DECLARE @sql NVARCHAR(MAX)
SELECT @sql = isnull(@sql + ',', 'select ')
+ quotename(fieldname) + '=' + convert(varchar(10), startpos)
FROM cteHour
order by startpos
print @sql
exec sp_executesql @sql
下面的查询应该做你想做的,它使用临时 table 但你会了解该方法是怎样的
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + @interval AS startpos
FROM cteHour
WHERE (startpos + @interval) < @period
)
SELECT * INTO #temp FROM cteHour;
DECLARE @pvt NVARCHAR(MAX) = '';
SET @pvt = STUFF(
(SELECT DISTINCT N', ' + QUOTENAME([fieldname]) FROM #temp FOR XML PATH('')),1,2,N'')
EXEC (N'SELECT *
FROM #temp
PIVOT (MAX([startpos]) FOR [fieldname] IN('+@pvt+')) AS PIV');
这是使用动态查询的最简单答案
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
DECLARE @startpos INT = 0
DECLARE @i INT = 0
DECLARE @sql NVARCHAR(MAX) = 'SELECT '
WHILE @startpos < @period
BEGIN
SET @sql = @sql + (CASE WHEN @i > 0 THEN ',' ELSE '' END) +
CAST(@startpos AS VARCHAR(5)) + ' AS i' + CAST(@i AS VARCHAR(5))
SET @i = @i + 1
SET @startpos = @startpos + @interval
END
print @sql
exec sp_executesql @sql
我有一个 CTE,可以将数据生成可变行数。这些是我需要在连接到另一个 table 时使用的小时值,但这部分在这里并不重要。
单独的 CTE 部分如下所示:
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + @interval AS startpos
FROM cteHour
WHERE (startpos + @interval) < @period
) SELECT fieldname, startpos FROM cteHour
它生成这样的数据(取决于 @interval
和 @period
值):
fieldname startpos
--------- -----------
i0 0
i1 3
i2 6
i3 9
i4 12
i5 15
i6 18
i7 21
我的问题是如何使用第一列作为字段名称将结果放入列而不是行中,如下所示:
i0 i1 i2 i3 i4 i5 i6 i7
-- -- -- -- -- -- -- --
0 3 6 9 12 15 18 21
我猜它需要 PIVOT
,但据我所知,旋转不能动态生成字段名称。所以我愿意接受任何能完成工作的答案。
因为它需要动态 sql,请跳过数据透视部分并直接从您的 cte
生成动态查询DECLARE @sql NVARCHAR(MAX)
SELECT @sql = isnull(@sql + ',', 'select ')
+ quotename(fieldname) + '=' + convert(varchar(10), startpos)
FROM cteHour
order by startpos
print @sql
exec sp_executesql @sql
下面的查询应该做你想做的,它使用临时 table 但你会了解该方法是怎样的
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + @interval AS startpos
FROM cteHour
WHERE (startpos + @interval) < @period
)
SELECT * INTO #temp FROM cteHour;
DECLARE @pvt NVARCHAR(MAX) = '';
SET @pvt = STUFF(
(SELECT DISTINCT N', ' + QUOTENAME([fieldname]) FROM #temp FOR XML PATH('')),1,2,N'')
EXEC (N'SELECT *
FROM #temp
PIVOT (MAX([startpos]) FOR [fieldname] IN('+@pvt+')) AS PIV');
这是使用动态查询的最简单答案
DECLARE @interval INT = 3 -- hours
DECLARE @period INT = 24 -- hours
DECLARE @startpos INT = 0
DECLARE @i INT = 0
DECLARE @sql NVARCHAR(MAX) = 'SELECT '
WHILE @startpos < @period
BEGIN
SET @sql = @sql + (CASE WHEN @i > 0 THEN ',' ELSE '' END) +
CAST(@startpos AS VARCHAR(5)) + ' AS i' + CAST(@i AS VARCHAR(5))
SET @i = @i + 1
SET @startpos = @startpos + @interval
END
print @sql
exec sp_executesql @sql