【问题标题】:SQL Unpivot the row and get Order NumberSQL 反透视该行并获取订单号
【发布时间】:2018-01-03 14:19:18
【问题描述】:

我有一排有几个诊断代码。如何将列取消透视为行?我尝试了以下查询来取消透视,但我不确定如何获取列号DX_Order,例如DX01DX_Order=1。我相信 UNPIVOT. 会处理 NULL

当前表

+----+------+-------+-------+------+----+------+
| ID | Name | DX01  | DX02  | DX03 | .. | DX25 |
+----+------+-------+-------+------+----+------+
|  1 | John | 426S3 | G2634 | NULL | .. | NULL |
+----+------+-------+-------+------+----+------+

预期结果

+----+------+----------+-------+
| ID | Name | DX_Order |  DX   |
+----+------+----------+-------+
|  1 | John |        1 | 426S3 |
|  1 | John |        2 | G2634 |
+----+------+----------+-------+

查询

SELECT ID, Name, DX
FROM
(
  SELECT ID, Name, DX01, DX02, DX03...DX25
  FROM dbo.Table
) AS cp
UNPIVOT 
(
  DX FOR DXs IN (DX01, DX02, DX03....DX25)
) AS up;

【问题讨论】:

  • 如果你的数据总是有 dx 按升序排列而没有间隙,那么简单的 row_number() 在数据分区上就可以解决这个问题。

标签: sql sql-server pivot unpivot


【解决方案1】:

使用Cross Apply 代替unpivot。除了DX0n 列,您还可以添加订单列

SELECT ID, Name, DX_Order, DX
  FROM dbo.Table
  Cross apply (values (DX01,1),(DX02,2),(DX03,3),...(DX025,25)) cs (DX, DX_Order)
  Where DX is not null

DX0n 列中有一些NULL 值并且您仍然希望DX_Order 是序列时

SELECT ID,
       NAME,
       DX_Order = Row_number()OVER(ORDER BY DX_Order),
       DX
FROM   (SELECT ID, NAME, DX_Order, DX
        FROM   dbo.Table
               CROSS apply (VALUES (DX01,1),(DX02,2),(DX03,3),..(DX025,25)) cs (DX, DX_Order)
        WHERE  DX IS NOT NULL) a 

【讨论】:

    【解决方案2】:

    Pரதீப் 解决方案将是我的第一选择 (+1),而且性能肯定更高。

    但是,以下内容将动态反透视您的数据,而无需指定所有字段,也无需实际使用动态 SQL

    示例

    Declare @YourTable table (ID int,Name varchar(50),DX01 varchar(50),DX02 varchar(50),DX03 varchar(50))
    Insert Into @YourTable values
    (1,'John','426S3','G2634',null)
    
    
    Select A.ID
          ,A.Name
          ,DX_Order= C.ColNr
          ,DX = C.Value
     From @YourTable A
     Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
     Cross Apply (
                    Select ColNr = row_number() over (order by (Select null))
                          ,Field = a.value('local-name(.)','varchar(100)')
                          ,Value = a.value('.','varchar(max)') 
                     From  B.XMLData.nodes('/row')  as C1(n)
                     Cross Apply C1.n.nodes('./@*') as C2(a)
                     Where a.value('local-name(.)','varchar(100)') not in ('ID','Name')
                 ) C
    

    退货

    ID  Name    DX_Order    DX
    1   John    1           426S3
    1   John    2           G2634
    

    【讨论】:

      【解决方案3】:

      请像这样尝试。

      SELECT ID,DX_Order,DX
             FROM dbo.TableN
                      CROSS apply 
      (VALUES ([DX01],1),([DX02],2)) cs (DX,DX_Order)
      WHERE DX IS NOT NULL
      

      【讨论】:

        【解决方案4】:

        您也可以通过执行动态 SQL 查询来实现。

        查询

        declare @sql as varchar(max);
        
        select @sql = stuff((
                select ' union all select [ID], [Name], cast(replace(' 
                + char(39) + [column_name] + char(39) + ', ' + char(39) 
                + 'DX' + char(39) + ',' + char(39) + char(39) + ') as int) as [DX_Order],
                case when [' + [column_name] + '] is not null then [' + [column_name] 
                + '] else null end as [DX] from [your_table_name]'
                from information_schema.columns
                where table_name = 'your_table_name'
                and [column_name] like 'DX%'
                order by ordinal_position
                for xml path(''), type
            ).value('.', 'nvarchar(max)')
            , 1, 11, ''
        );
        
        select @sql = 'select t.* from (' + @sql + ') t where t.[DX] is not null;';
        
        exec(@sql); 
        

        【讨论】:

          【解决方案5】:

          样本数据创建

          IF OBJECT_ID('dbo.temp') iS NOT NULL
          DROP TABLE temp
          ;With cte(ID , Name , DX01  , DX02  , DX03 , DX25 )
          AS
          ( SELECT 1 , 'John' , '426S3' , 'G2634' , NULL,NULL 
          )
          SELECT * INTO temp FROM cte
          

          使用动态Sql,当我们有n列时,我们可以得到预期的结果

          DECLARE @SQlColumn nvarchar(max)
                  ,@SQl nvarchar(max)
          
          
          SELECT @SQlColumn= STUFF((SELECT ', '+Columnname  FROM
                          (
                      SELECT  '( '+ 'CAST('+QUOTENAME(COLUMN_NAME)+' AS VARCHAR(30)),CAST(REPLACE('''+COLUMN_NAME+''+''','''+'DX'+''',''' +''''+') AS VARCHAR(30)))'  AS Columnname
                          FROM INFORMATION_SCHEMA.COLUMNS 
                          WHERE TABLE_NAME='temp' AND COLUMN_NAME <>'Id' AND COLUMN_NAME <>'Name'
                          )dt FOR XML PATH ('')),1,1,'')
          
          SET @SQl='  SELECT ID
                           ,Name
                           ,DX
                           ,DX_Order FROM temp 
                      CROSS APPLY (VALUES '+ @SQlColumn + ') dt (DX,DX_Order) WHERE DX IS NOT NULL'
          PRINT @SQl
          EXEC(@SQl)
          

          结果

            ID    Name    DX_Order    DX
            ------------------------------
             1    John    01         426S3
             1    John    02         G2634
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-06-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-09
            • 2021-12-21
            • 1970-01-01
            相关资源
            最近更新 更多