【问题标题】:Columns to Rows with varying dates具有不同日期的列到行
【发布时间】:2018-10-24 06:34:31
【问题描述】:

请参阅下面的示例数据和所需的输出格式:

--SAMPLE TABLE
DECLARE @TEMP TABLE(
    DATA_DATE DATE,
    PROD_ID INT,
    CAT_CODE NVARCHAR(10),
    DATABUCKET_1 FLOAT,
    DATABUCKET_2 FLOAT,
    DATABUCKET_3 FLOAT,
    DATABUCKET_4 FLOAT,
    DATABUCKET_5 FLOAT);

INSERT INTO @TEMP VALUES('19-Oct-2018',100,'C1', 100,200,300,400,500)

SELECT * FROM @TEMP;

--PREFERRED OUTPUT FORMAT

SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
UNION ALL
SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
UNION ALL
SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
UNION ALL
SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
UNION ALL
SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4

补充几点:

  • 我的实际表有 106 个数据桶和其他几列。
  • 为了简单起见,我在这里只给出了几个。
  • 每个月都会收到一个具有不同 DATA_DATE 值的新文件。
  • DATA_DATE 值在一个文件中相同,对应DATABUCKET_1
  • 对于其他 DATABUCKETS,该值为一周前。

请告诉我如何使用 UNPIVOT 实现这一目标。在此先感谢

【问题讨论】:

    标签: sql sql-server tsql transpose unpivot


    【解决方案1】:

    使用动态 T-SQL 语句可以很容易地做到这一点。我们的想法是提前获取我们需要用于反透视的列,并为每列添加添加订单 ID。此数字将用于计算最后一列和日期列。

    注意,我已将@table variable 更改为normal table,以便能够从sys.columns 视图中动态读取列。当然,在您的真实示例中,您可以随意填充表格,也可以随意对列进行排序。

    --DROP TABLE IF EXISTS [dbo].[Temp];
    
    CREATE TABLE [dbo].[Temp](
        DATA_DATE DATE,
        PROD_ID INT,
        CAT_CODE NVARCHAR(10),
        DATABUCKET_1 FLOAT,
        DATABUCKET_2 FLOAT,
        DATABUCKET_3 FLOAT,
        DATABUCKET_4 FLOAT,
        DATABUCKET_5 FLOAT);
    
    INSERT INTO [dbo].[Temp] VALUES ('19-Oct-2018',100,'C1', 100,200,300,400,500)
    
    SELECT * FROM [dbo].[Temp];
    
    --PREFERRED OUTPUT FORMAT
    
    SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
    UNION ALL
    SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
    UNION ALL
    SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
    UNION ALL
    SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
    UNION ALL
    SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4
    
    
    DECLARE @DynamicTSQLStatement NVARCHAR(MAX)
           ,@ColumnNames NVARCHAR(MAX);
    
    --DROP TABLE IF EXISTS #Columns;
    
    CREATE TABLE #Columns
    (
        [ID] INT
       ,[name] SYSNAME
    );
    
    INSERT INTO #Columns ([ID], [name])
    SELECT ROW_NUMBER() OVER (ORDER BY [column_id]) - 1
          ,[name]
    FROM [sys].[columns]
    WHERE [object_id] = OBJECT_ID('[dbo].[Temp]')
        AND [name] LIKE '%DATABUCKET%';
    
    SELECT @ColumnNames = STUFF
    (
        (
            SELECT ',[' + [name] + ']'
            FROM #Columns
            ORDER BY [id]
            FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)')
       ,1
       ,1
       ,''
    );
    
    
    SET @DynamicTSQLStatement = N'
    SELECT [CAT_CODE]
          ,DATEADD(WEEK, -1 * C.id, DATA_DATE) AS DATA_DATE
          ,value as UNITS
          ,''W'' + CAST(c.id + 1 AS VARCHAR(8)) as [WEEK_NUM]
    FROM [dbo].[Temp] 
    UNPIVOT
    (
        [value] FOR [column] IN ('+ @ColumnNames +')
    ) UNPVT
    INNER JOIN #Columns C
        ON UNPVT.[column] = c.[name] 
    ORDER BY DATA_DATE DESC
    ;'
    
    EXEC sp_executesql @DynamicTSQLStatement;
    

    所以,这就是想法。由操作代码来处理您的数据。

    【讨论】:

      【解决方案2】:

      您可以使用CROSS APPLY 执行un-pivot

      SELECT  t.CAT_CODE, d.*
      FROM    @TEMP t
          CROSS APPLY
          (
              SELECT  DATA_DATE = t.DATA_DATE, UNITS = t.DATABUCKET_1, WEEK_NUM = 'W1'    union all
              SELECT  DATA_DATE = DATEADD(DAY, -7, t.DATA_DATE), UNITS = t.DATABUCKET_2, WEEK_NUM = 'W2'  union all
              SELECT  DATA_DATE = DATEADD(DAY, -14, t.DATA_DATE), UNITS = t.DATABUCKET_3, WEEK_NUM = 'W3' union all
              SELECT  DATA_DATE = DATEADD(DAY, -21, t.DATA_DATE), UNITS = t.DATABUCKET_4, WEEK_NUM = 'W4' union all
              SELECT  DATA_DATE = DATEADD(DAY, -28, t.DATA_DATE), UNITS = t.DATABUCKET_5, WEEK_NUM = 'W5'
          ) d
      

      或使用计数/数字表

      SELECT  t.CAT_CODE, DATA_DATE = DATEADD(DAY, -7 * n, t.DATA_DATE),
          UNITS   = CASE n
                  WHEN 0 THEN t.DATABUCKET_1
                  WHEN 1 THEN t.DATABUCKET_2
                  WHEN 2 THEN t.DATABUCKET_3
                  WHEN 3 THEN t.DATABUCKET_4
                  WHEN 4 THEN t.DATABUCKET_5
                  END,
          WEEK_NUM = 'W' + CONVERT(VARCHAR(10), n + 1)
      FROM    @TEMP t
          INNER JOIN NUMBERS n    ON  n   between 0 and 4
      

      如果你真的有 106 个桶,你真的应该考虑标准化你的表。否则,您需要对 106 行重复上述操作。另一种方式是使用Dynamic SQL来处理

      【讨论】:

        猜你喜欢
        • 2021-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多