2009-06-16 54 views
16

UNPIVOT不会返回NULL,但我需要它们进行比较查询。我试图避免使用ISNULL下面的例子(因为在实际的SQL有超过100个字段:SQL Server - 使用UNPIVOT包含NULL

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 

其它方法吗?

回答

11

这是一个真正的痛苦。你必须之前转出来UNPIVOT,因为没有为ISNULL()生产经营上排 - 代码生成是你的朋友在这里

我有PIVOT问题以及缺少的行变成NULL,你有ISNULL()包裹一路。如果缺少,则横跨该行例如,值与0.0相同。

+0

CROSS JOIN ... CASE将保留空值。见下面的例子。 – 2009-06-17 13:47:55

15

要保留空值,使用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 from table1 
) a 
unpivot (
    column_value FOR column_name IN (
    col1, col2, col3, col4) 
) b 

与列模式下的文本编辑器,使得这样的查询更容易编写。 UltraEdit拥有它,Emacs也是如此。在Emacs中,它被称为矩形编辑。

您可能需要将其脚本编写为100列。在较短的方式

+0

我想为它编写5个以上的列,但我很懒:-)。下面是一个例子:`select'select'''+ column_name +'''UNION ALL'FROM information_schema。列WHERE table_name ='table1'和table_schema ='dbo'` – Anssssss 2015-12-28 20:18:11

3

,或者在2008年的SQLServer:

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

使用动态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) 
2

我发现左外加入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  |  Value   | 
+----------------------+-----------------------+ 
| name     | blah1     | 
| object_id   | 3      | 
| principal_id   | NULL     | <====== 
| schema_id   | 4      | 
| parent_object_id  | 0      | 
| type     | blah2     | 
| type_desc   | blah3     | 
| create_date   | 20100402 16:59:23.26 | 
| modify_date   | NULL     | <====== 
| is_ms_shipped  | 1      | 
| is_published   | 0      | 
| is_schema_published | 0      | 
+----------------------+-----------------------+ 

+0

这唯一的缺点是所有源字段必须连续输入。 – Ozziemedes 2014-11-21 00:01:13

+0

@Ozziemedes我认为你可能会忽略这一点,即使OP将原始数据转换为varchar ...在所有这些技术的某一点上,原始类型都必须丢失,这是由于unpivot的基本性质返回了先前的不同的列,在一个列下。另外,你确定你正确使用“连续”这个词吗?这个词的定义意思是“彼此相邻”,而不是像你想要的那样“相同”? – Beej 2014-11-22 02:12:57

-1

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”,但对未来的程序员可能会更困惑。我相信你会得到这张照片。

2

我遇到了同样的问题,使用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(Measure, MeasureType) 


SELECT OrderDate, product,employee,Measure,MeasureType 
from #Orders orders 
UNPIVOT 
    (Measure FOR MeasureType IN 
     (ItemsCount,GrossAmount) 
)AS unpvt; 


drop table #Orders