【问题标题】:SQL convert rows generated by CTE to columns - including fieldnamesSQL 将 CTE 生成的行转换为列 - 包括字段名
【发布时间】:2019-11-20 16:34:51
【问题描述】:

我有一个 CTE,可以将数据生成到可变数量的行中。这些是我需要在连接到另一个表时使用的每小时值,但这部分在这里并不重要。

单独的 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,但据我所知,旋转无法动态生成字段名称。所以我愿意接受任何可以完成工作的答案。

【问题讨论】:

  • 由于@interval@period 是动态的,您将需要使用Dynamic SQL。您可能需要重新考虑这将如何在您的完整解决方案中发挥作用

标签: sql-server pivot common-table-expression


【解决方案1】:

由于它需要动态 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

【讨论】:

    【解决方案2】:

    下面的查询应该做你想做的事,它使用一个临时表,但你会知道这个方法是如何的

    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');
    

    【讨论】:

      【解决方案3】:

      这是使用动态查询的最简单答案

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-22
        • 2021-11-24
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多