【问题标题】:SQL Server - Include NULL using UNPIVOTSQL Server - 使用 UNPIVOT 包含 NULL
【发布时间】:2010-11-03 10:20:52
【问题描述】:

UNPIVOT 不会返回NULLs,但我需要它们来比较query。我试图避免使用ISNULL下面的例子(因为在真正的sql中有超过100个字段):

Select ID, theValue, column_name
From 
(select ID,
  ISNULL(CAST([TheColumnToCompare]  AS VarChar(1000)), '') as TheColumnToCompare
  from MyView
  where The_Date = '04/30/2009'
) MA
UNPIVOT
   (theValue FOR column_name IN 
   ([TheColumnToCompare])
) AS unpvt

还有其他选择吗?

【问题讨论】:

    标签: sql-server tsql null unpivot


    【解决方案1】:

    真的很痛苦。您必须在UNPIVOT 之前将它们切换出去,因为没有为ISNULL() 操作生成的行 - 代码生成在这里是您的朋友。

    PIVOT 也有问题。缺失的行会变成NULL,如果缺失值与0.0 相同,则必须将其一直包裹在ISNULL() 中。

    【讨论】:

    • CROSS JOIN ... CASE 将保留空值。请参阅下面的示例。
    【解决方案2】:

    要保留 NULL,请使用 CROSS JOIN ... CASE:

    select a.ID, b.column_name
    , column_value = 
        case b.column_name
          when 'col1' then a.col1
          when 'col2' then a.col2
          when 'col3' then a.col3
          when 'col4' then a.col4
        end
    from (
      select ID, col1, col2, col3, col4 
      from table1
      ) a
    cross join (
      select 'col1' union all
      select 'col2' union all
      select 'col3' union all
      select 'col4'
      ) b (column_name)
    

    代替:

    select ID, column_name, column_value
    From (
      select ID, col1, col2, col3, col4
      from table1
      ) a
    unpivot (
      column_value FOR column_name IN (
        col1, col2, col3, col4)
      ) b
    

    具有列模式的文本编辑器使此类查询更易于编写。 UltraEdit 有,Emacs 也有。在 Emacs 中称为矩形编辑。

    您可能需要为 100 列编写脚本。

    【讨论】:

    • 我会编写超过 5 列的脚本,但我很懒 :-)。这是一个示例:select 'select ''' + column_name + ''' UNION ALL' FROM information_schema.columns WHERE table_name = 'table1' and table_schema = 'dbo'
    【解决方案3】:

    或者,在 SQLServer 2008 中更短的方式:

    ...
    cross join 
    (values('col1'), ('col2'), ('col3'), ('col4')) column_names(column_name)
    

    【讨论】:

      【解决方案4】:

      使用动态 SQL 和 COALESCE,我解决了这样的问题:

      DECLARE @SQL NVARCHAR(MAX)
      DECLARE @cols NVARCHAR(MAX)
      DECLARE @dataCols NVARCHAR(MAX)
      
      SELECT 
          @dataCols = COALESCE(@dataCols + ', ' + 'ISNULL(' + Name + ',0) ' + Name , 'ISNULL(' + Name + ',0) ' + Name )
      FROM Metric WITH (NOLOCK)
      ORDER BY ID
      
      SELECT 
          @cols = COALESCE(@cols + ', ' + Name , Name )
      FROM Metric WITH (NOLOCK)
      ORDER BY ID
      
      SET @SQL = 'SELECT ArchiveID, MetricDate, BoxID, GroupID, ID MetricID, MetricName, Value
                  FROM 
                     (SELECT ArchiveID, [Date] MetricDate, BoxID, GroupID,  ' + @dataCols + '
                      FROM MetricData WITH (NOLOCK)
                      INNER JOIN Archive WITH (NOLOCK)
                          ON ArchiveID = ID
                      WHERE BoxID = ' + CONVERT(VARCHAR(40), @BoxID) + '
                      AND GroupID = ' + CONVERT(VARCHAR(40), @GroupID) + ') p
                  UNPIVOT
                     (Value FOR MetricName IN 
                        (' + @cols + ')
                  )AS unpvt
                  INNER JOIN Metric WITH (NOLOCK)
                      ON MetricName  = Name
                  ORDER BY MetricID, MetricDate'
      
      EXECUTE( @SQL )
      

      【讨论】:

        【解决方案5】:

        我发现左外连接 UNPIVOT 结果到完整的字段列表,方便地从 INFORMATION_SCHEMA 中提取,在某些情况下是解决此问题的实用答案。

        -- test data
        CREATE TABLE _t1(name varchar(20),object_id varchar(20),principal_id varchar(20),schema_id varchar(20),parent_object_id varchar(20),type varchar(20),type_desc varchar(20),create_date varchar(20),modify_date varchar(20),is_ms_shipped varchar(20),is_published varchar(20),is_schema_published varchar(20))
        INSERT INTO _t1 SELECT 'blah1', 3, NULL, 4, 0, 'blah2', 'blah3', '20100402 16:59:23.267', NULL, 1, 0, 0 
        
        -- example
        select c.COLUMN_NAME, Value
        from INFORMATION_SCHEMA.COLUMNS c
        left join (
          select * from _t1
        ) q1
        unpivot (Value for COLUMN_NAME in (name,object_id,principal_id,schema_id,parent_object_id,type,type_desc,create_date,modify_date,is_ms_shipped,is_published,is_schema_published)
        ) t on t.COLUMN_NAME = c.COLUMN_NAME
        where c.TABLE_NAME = '_t1'
        </pre>
        

        输出看起来像:

        +----------+---------- +
        | COLUMN_NAME |价值 |
        +----------------------------------+----------+
        |姓名 |废话1 |
        | object_id | 3 |
        | principal_id |空 | 
            

        【讨论】:

        • 唯一的缺点是所有源字段都必须连续输入。
        • @Ozziemedes 我认为您可能错过了重点,即使 OP 将原始数据转换为 varchar ......在所有这些技术的某个时刻,由于基本性质,原始类型必须丢失一个 unpivot 返回以前不同的列,在一个列下。另外,你确定你正确使用了“连续”这个词吗?这个词的定义是指“彼此相邻”,而不是您想要的“相同”?
        • 哈哈。在上下文中:当我遇到这篇文章时,我正在寻找一种方法来取消强类型数据的透视。我没有提出批评——只是一个观察。我当然没想到我的头会被扯掉,但“欢迎来到互联网”等等。 叹息
        【解决方案6】:

        ISNULL 是答案的一半。使用 NULLIF 转换回 NULL。例如。

        DECLARE @temp TABLE(
            Foo varchar(50),
            Bar varchar(50) NULL
            );
        
        INSERT INTO @temp( Foo,Bar )VALUES( 'licious',NULL );
        
        SELECT * FROM @temp;
        
        SELECT 
            Col,
            NULLIF( Val,'0Null' ) AS Val 
        FROM(
            SELECT
                Foo,
                ISNULL( Bar,'0Null' ) AS Bar
            FROM
                @temp
            ) AS t
        UNPIVOT(
            Val FOR Col IN(
                Foo,
                Bar 
                )
            ) up;
        

        这里我使用“0Null”作为我的中间值。你可以使用任何你喜欢的东西。但是,如果您选择诸如“Null”之类的真实世界内容,则可能会与用户输入发生冲突。垃圾可以正常工作“!@#34())0”,但可能会让未来的编码人员更加困惑。我相信你明白了。

        【讨论】:

        • OP 询问的是unpivot,而不是pivot。如果可能,请修改您的答案。
        • 如果您对此主题有所了解.. 您会明白该解决方案同样适用于 UNPIVOT。在您要求其他人撤回与 PIVOT 或 UNPIVOT 无关的解决方案之前,我不会进行微不足道的更改,例如交叉连接、动态 SQL 和审计员笑话。事实是,我的解决方案是唯一的,它为原始问题提供了答案。
        • 顶部的答案运行良好,足以让 OP 被接受。不管琐碎(作为一名每天使用 MSSQL 的分析师,我确实意识到这一点),您都未能根据the help center 中定义的本网站规则回答问题。其他人的帖子并不规定什么是/不允许的。我并不是想在这里成为一个混蛋——社区专门委托我审查这篇文章,因为这是你的第一篇文章。
        • 我非常了解如何回答所提出的具体问题。我认为其他海报违反了您的规则。在我提出我的解决方案之前,我持有这种观点。想象一下当我被标记时我的惊讶!无论如何,我不会改变我的职位。如果必须或删除它,请给它一个 -1。但是,我认为其他任何人来此站点寻求有关如何在 UNPIVOT 或 PIVOT 中保留 NULL 的答案都会发现我的答案很有帮助。
        • 某些其他帖子也被标记了。编辑可能很小,但它很重要......我们有很多新手来这里寻求答案,并且可能不会立即看到您的答案与初始帖子之间的联系。如果您进行编辑,我将能够删除 -1,并且分配用于审查我的标志的模块可能会拒绝它。
        【解决方案7】:

        我遇到了同样的问题。使用CROSS APPLY(SQL Server 2005 及更高版本)而不是Unpivot 解决了这个问题。我根据这篇文章找到了解决方案An Alternative (Better?) Method to UNPIVOT 我做了下面的例子来证明 CROSS APPLY 不会忽略像 Unpivot 这样的空值。

        create table #Orders (OrderDate datetime, product nvarchar(100), ItemsCount float, GrossAmount float, employee nvarchar(100))
        
         insert into #Orders
         select getutcdate(),'Windows',10,10.32,'Me'
         union 
         select getutcdate(),'Office',31,21.23,'you'
         union 
         select getutcdate(),'Office',31,55.45,'me'
         union  
         select getutcdate(),'Windows',10,null,'You'
        
        SELECT OrderDate, product,employee,Measure,MeasureType
         from #Orders orders
         CROSS APPLY (
            VALUES ('ItemsCount',ItemsCount),('GrossAmount',GrossAmount)
            ) 
            x(MeasureType, Measure) 
        
        
        SELECT OrderDate, product,employee,Measure,MeasureType
        from #Orders orders
        UNPIVOT
           (Measure FOR MeasureType IN 
              (ItemsCount,GrossAmount)
        )AS unpvt;
        
        
         drop table #Orders
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-18
          • 1970-01-01
          • 1970-01-01
          • 2017-11-24
          • 2016-05-11
          相关资源
          最近更新 更多