【问题标题】:PIVOT SQL Table by Date, Name and Value按日期、名称和值的 PIVOT SQL 表
【发布时间】:2018-09-09 19:31:28
【问题描述】:

我有一个查询返回给我的数据,其中频道名称存在于另一个表中,所以我只返回一组特定的频道。

SELECT DISTINCT 
    ReadingDate, SerialNumber, ChannelName, ChannelValue
FROM 
    [Staging].[UriData]
WHERE 
    ChannelName IN (SELECT ChannelName FROM [dbo].[Channels])
ORDER BY 
    ReadingDate DESC, ChannelName

此查询的重要性在于它只带回存储移动数据而不是固定诊断数据的通道。

结果(sn-p):

ReadingDate         | SerialNumber | ChannelName |  ChannelValue
2018-09-09 20:30:00     2209            m1            461
2018-09-09 20:30:00     2209            m2            0
2018-09-09 20:30:00     2209            m3            50
2018-09-09 20:30:00     2209            m4            15631
2018-09-09 20:30:00     2209            m5            1
2018-09-09 20:30:00     2209            m6            8150
2018-09-09 20:30:00     2209            m7            0
2018-09-09 20:30:00     2209            m8            2790
2018-09-09 20:30:00     2209            m9            0
2018-09-09 20:15:00     2209            m1            2452
2018-09-09 20:15:00     2209            m2            0
2018-09-09 20:15:00     2209            m3            50
2018-09-09 20:15:00     2209            m4            15629
2018-09-09 20:15:00     2209            m5            1
2018-09-09 20:15:00     2209            m6            8100
2018-09-09 20:15:00     2209            m7            0
2018-09-09 20:15:00     2209            m8            2780

然后我想将这些数据转换为列,以便它们按天(日期)分组,然后从该日期开始计算时间。

需要的输出:

DATE        | SERIAL | ChannelName |   00:15 |  00:30   | ..... | 23:45
2018-09-06    2209         m1          Value    Value    .....    Value
2018-09-06    2209         m2          Value    Value    .....    Value

我一直在玩枢轴,但我没有得到我所描述的格式的数据。

【问题讨论】:

  • 可以向我们展示表格中的样本数据而不是查询结果。
  • 为什么要创建多个Time列有什么逻辑吗?
  • D-Shih,我可以在任何地方发布完整的数据集,因为它目前有 1780 行。我想这就是你的意思。
  • 关于时间,日期格式为'2018-09-09 20:00:00'。每个 ChannelName 每天将有 96 个读数。所以我需要在第一个时间列中的 00:15 获得第一个读数,然后在同一天的午夜获得最后一个读数,所有其他时间都介于两者之间,例如00:15、00:30、00:45 等
  • 如果您知道每天有 96 个“读数”,串行和频道,从 00:00 开始到 23:45 结束,您出于什么原因将这些信息存储在数据库表中?

标签: sql sql-server pivot


【解决方案1】:

您可以尝试使用cte recursion制作时间日历表,然后通过time制作行号。

然后使用带有pivot的动态SQL来实现。

这是给您的示例。

DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX);


;WITH CTE AS (
    SELECT CAST('00:00' AS TIME) startDt, CAST('23:45' AS TIME) endDt
    UNION ALL 
    SELECT DATEADD(MINUTE, 15, startDt),endDt
    FROM CTE
    WHERE DATEADD(MINUTE, 15, startDt) <endDt
), TimeTable AS (
    select *,ROW_NUMBER() OVER (ORDER BY startDt) rn 
    FROM (
       SELECT  startDt,endDt
       FROM CTE
       UNION ALL 
       SELECT CAST('23:45' AS TIME) startDt, CAST('23:45' AS TIME) endDt
    ) t1
)



select @cols = CONCAT(@cols,'MAX(CASE WHEN '''+CAST(startDt AS VARCHAR(5))+''' = CAST(ReadingDate AS TIME) THEN ChannelValue ELSE 0 end) AS ',QUOTENAME(CAST(startDt AS VARCHAR(5))),', ')
from TimeTable
WHERE startDt <= endDt
ORDER BY rn 

SET @cols = left(@cols, len(@cols) - 1)


set @query = '
;WITH CTE AS ( SELECT CAST('''+ cast('00:00' as varchar(5))+''' AS TIME) startDt, CAST('''+ cast('23:45' as varchar(5))+''' AS TIME) endDt
    UNION ALL 
    SELECT DATEADD(MINUTE, 15, startDt),endDt
    FROM CTE
    WHERE DATEADD(MINUTE, 15, startDt) <endDt
), TimeTable AS (
    select *,ROW_NUMBER() OVER (ORDER BY startDt) rn 
    FROM (
       SELECT  startDt,endDt
       FROM CTE
       UNION ALL 
       SELECT CAST('''+ cast('23:45' as varchar(5))+''' AS TIME) startDt, CAST('''+ cast('23:45' as varchar(5))+''' AS TIME) endDt
    ) t1
)

SELECT CONVERT(VARCHAR(10),ReadingDate, 126),
      SerialNumber,
      ChannelName,
       '+ @cols +'
FROM  T
GROUP BY CONVERT(VARCHAR(10),ReadingDate, 126) ,SerialNumber,ChannelName
'

exec(@query)

sqlfiddle

【讨论】:

  • 这就是我想要的。我编辑了问题,因为我在所需的输出中出错了。我要求值在名称列下。
  • 哇,效果非常好。它比我想象的要复杂得多。我不是 SQL 专家,你可能会说...
  • 如果你不想那么复杂,你可以只使用简单的支点,但需要做很多事情..
  • 谢谢...答案是正确的,谢谢您的建议。
  • 我的回答比另一个早。而且我认为我的解决方案是更具弹性
【解决方案2】:

您也可以使用PIVOT 构造实现相同的结果,如下所示:

SELECT [Serial], [Channel], [ReadingDate], [00:15], [00:30], ....
FROM(
    SELECT 
        SerialNumber AS [Serial],
        ChannelName AS [Channel],
        CAST(ReadingDate AS DATE) AS [ReadingDate],
        CAST(ReadingDate AS TIME(0)) AS [ReadingTime],
        ChannelValue AS [Value]
    FROM [Staging].[UriData]
    WHERE
        ChannelName IN (SELECT ChannelName FROM Staging.ActiveChannels )
        AND Processed = 0
    ) AS [Raw]
PIVOT
(
    MAX( [Value] ) FOR [ReadingTime] IN( [00:15], [00:30], .... )
) AS pvt
ORDER BY ReadingDate DESC, Channel, [Serial]

要生成“时间”列(让您不必手动输入),请运行以下查询:

DECLARE @cols AS NVARCHAR(MAX)
;WITH Times AS (
    SELECT CAST( '00:00' AS TIME) AS [Time]
    UNION ALL 
    SELECT DATEADD( MINUTE, 15, [Time] )
    FROM Times
    WHERE [Time] < CAST( '23:45' AS TIME )
)
--SELECT * FROM Times
SELECT @cols = CONCAT( @cols, QUOTENAME( CAST( [Time] AS VARCHAR( 5 ) )), ', ' )
FROM Times

SET @cols = LEFT( @cols, LEN( @cols ) - 1 )
PRINT @cols

将结果复制/粘贴到上面的“PIVOT”查询中。

【讨论】:

    猜你喜欢
    • 2021-09-29
    • 1970-01-01
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多