【问题标题】:Unpivotting multiple columns - substring of column name as a new column with CROSS APPLY取消透视多个列 - 列名的子字符串作为具有 CROSS APPLY 的新列
【发布时间】:2025-11-25 08:20:06
【问题描述】:

我有一个格式如下的表格

YEAR, MONTH, ITEM,  REQ_QTY1, REQ_QTY2 , ....REQ_QTY31 ,CONVERTED1, CONVERTED2 ....CONVERTED31

每列的后缀是月份中的哪一天。

我需要将其转换为以下格式,其中Day_of_month为每一列的数字后缀

YEAR, MONTH, DAY_OF_MONTH, ITEM, REQ_QTY, CONVERTED

我想过使用 CROSS APPLY 来检索数据,但是我无法使用 CROSS APPLY 来获取“日期”

SELECT A.YEAR, A.MONTH, A.ITEM, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(VALUES
   (REQ_QTY1, CONVERTED1),
 (REQ_QTY2, CONVERTED2),
 (REQ_QTY3, CONVERTED3),
......
 (REQ_QTY31, CONVERTED31)
 )B (REQ_QTY, CONVERTED)

我发现的唯一方法是使用带有内连接的嵌套选择

SELECT A.YEAR, A.MONTH,  A.DAY_OF_MONTH,  A.ITEM,A.REQ_QTY, D.CONVERTED FROM
  (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,8,2) AS DAY_OF_MONTH, REQ_QTY FROM TEST 
  UNPIVOT
     (REQ_QTY FOR DAY_OF_MONTH IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],......[REQ_QTY30],[REQ_QTY31])
     ) B
  ) A
  INNER JOIN (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,10,2) AS DAY_OF_MONTH, CONVERTED FROM TEST 
  UNPIVOT
     (CONVERTED FOR DAY_OF_MONTH IN ([CONVERTED1],[CONVERTED2],[CONVERTED3],....[CONVERTED30],[CONVERTED31])
     ) C
  ) D
  ON D.YEAR = A.YEAR AND D.MONTH = A.MONTH AND D.ITEM = A.ITEM AND D.DAY_OF_MONTH = A.DAY_OF_MONTH

有没有办法使用 CROSS APPLY 却又把 DAY_OF_MONTH 拿出来?

【问题讨论】:

    标签: sql-server unpivot cross-apply


    【解决方案1】:

    这不是CROSS APPLY 的解决方案,但它肯定会使其更快一点,因为它使用更简单的方法和更简单的执行计划。

    SQL Fiddle

    MS SQL Server 2008 架构设置

    CREATE TABLE Test_Table([YEAR] INT, [MONTH] INT, [ITEM] INT,  REQ_QTY1 INT
           , REQ_QTY2 INT ,REQ_QTY3 INT , CONVERTED1 INT, CONVERTED2 INT, CONVERTED3 INT)
    INSERT INTO Test_Table VALUES 
    ( 2015 ,  1 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
    ( 2015 ,  2 , 1 , 10 , 20 , 30 , 100 , 200 , 300), 
    ( 2015 ,  3 , 1 , 10 , 20 , 30 , 100 , 200 , 300)
    

    查询 1

    SELECT *
    FROM 
     (
        SELECT [YEAR] 
              ,[MONTH] 
              ,ITEM
              ,Vals
              ,CASE WHEN LEFT(N,3) = 'REQ' THEN SUBSTRING(N,8 ,2)
                    WHEN LEFT(N,3) = 'CON' THEN SUBSTRING(N,10,2)
               END AS Day_Of_Month
              ,CASE WHEN LEFT(N,3) = 'REQ' THEN LEFT(N,7)
                    WHEN LEFT(N,3) = 'CON' THEN LEFT(N,9)
               END AS Tran_Type
        FROM Test_Table t
          UNPIVOT (Vals FOR N IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],
                                                [CONVERTED1],[CONVERTED2],[CONVERTED3]))up
     )t2
    PIVOT (SUM(Vals)
           FOR Tran_Type
           IN (REQ_QTY, CONVERTED))p  
    

    Results

    | YEAR | MONTH | ITEM | Day_Of_Month | REQ_QTY | CONVERTED |
    |------|-------|------|--------------|---------|-----------|
    | 2015 |     1 |    1 |            1 |      10 |       100 |
    | 2015 |     1 |    1 |            2 |      20 |       200 |
    | 2015 |     1 |    1 |            3 |      30 |       300 |
    | 2015 |     2 |    1 |            1 |      10 |       100 |
    | 2015 |     2 |    1 |            2 |      20 |       200 |
    | 2015 |     2 |    1 |            3 |      30 |       300 |
    | 2015 |     3 |    1 |            1 |      10 |       100 |
    | 2015 |     3 |    1 |            2 |      20 |       200 |
    | 2015 |     3 |    1 |            3 |      30 |       300 |
    

    【讨论】:

      【解决方案2】:

      好吧,我找到了一种使用 CROSS APPLY 的方法,但我基本上不是采用子字符串,而是对日子进行硬编码。效果很好,所以...

      SELECT A.YEAR, A.MONTH, A.ITEM, B.DAY_OF_MONTH, B.REQ_QTY, B.CONVERTED
      FROM TEST A
      CROSS APPLY
      (
        VALUES
        ('01', REQ_QTY1, CONVERTED1),
        ('02', REQ_QTY2, CONVERTED2),
        ('03', REQ_QTY3, CONVERTED3),
        ('04', REQ_QTY4, CONVERTED4),
         ......
        ('31', REQ_QTY31, CONVERTED31)
        ) B (DAY_OF_MONTH, REQ_QTY, CONVERTED)
      

      【讨论】:

        最近更新 更多