2009-06-16 36 views
16

UNPIVOT sẽ không trả về NULL, nhưng tôi cần chúng trong một truy vấn so sánh. Tôi cố gắng để tránh sử dụng ISNULL ví dụ sau (Bởi vì trong sql thực có hơn 100 lĩnh vực .:Máy chủ SQL - Bao gồm NULL sử dụng UNPIVOT

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 

Bất kỳ lựa chọn thay thế?

Trả lời

11

Đó là một nỗi đau thực sự. Bạn phải chuyển chúng ra trước UNPIVOT, vì không có hàng sản xuất cho ISNULL() để hoạt động trên - thế hệ mã là bạn của bạn ở đây

tôi có vấn đề về PIVOT cũng hàng Thiếu biến thành NULL, mà bạn phải quấn trong ISNULL() tất cả các cách.. trên hàng nếu thiếu các giá trị giống như ví dụ 0.0.

+0

CROSS JOIN ... CASE sẽ giữ lại giá trị rỗng. Xem ví dụ bên dưới. –

15

Để duy trì NULLs, sử dụng CROSS JOIN ... HỒ SƠ:

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) 

Thay vì:

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 

Một soạn thảo văn bản với chế độ cột làm cho truy vấn như vậy dễ dàng hơn để viết. UltraEdit có nó, do đó, Emacs. Trong Emacs nó được gọi là chỉnh sửa hình chữ nhật.

Bạn có thể cần phải viết mã cho 100 cột.

+0

Tôi sẽ viết kịch bản cho hơn 5 cột, nhưng tôi lười như vậy :-). Đây là một ví dụ: 'select 'select' '' + column_name + '' 'UNION ALL' FROM information_schema.cột WHERE table_name = 'table1' và table_schema = 'dbo'' – Anssssss

3

hoặc, trong SQLServer 2008 tại đường ngắn hơn:

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

Sử dụng SQL năng động và liên hiệp, tôi giải quyết vấn đề như thế này:

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

Tôi đã tìm thấy trái bên ngoài tham gia kết quả UNPIVOT vào danh sách đầy đủ các trường, được thuận tiện lấy từ INFORMATION_SCHEMA, là một câu trả lời thực tế cho vấn đề này trong một số ngữ cảnh.

-- 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> 

đầu ra trông giống như:

+----------------------+-----------------------+ 
| 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

Nhược điểm duy nhất của việc này là tất cả các trường nguồn của bạn phải được nhập liên tục. – Ozziemedes

+0

@Ozziemedes Tôi nghĩ rằng bạn có thể bị thiếu điểm, ngay cả OP phôi dữ liệu ban đầu để varchar ... tại một số điểm trong tất cả các kỹ thuật này, các loại ban đầu phải bị mất do bản chất cơ bản của một unpivot trả lại những gì trước đây các cột khác nhau, dưới một cột. Ngoài ra, bạn có chắc là bạn đang sử dụng từ "tiếp giáp" đúng không? Định nghĩa của từ đó có nghĩa là "bên cạnh nhau", không phải "giống nhau" giống như bạn dự định? – Beej

+0

LOL. Trong ngữ cảnh: Tôi đang tìm cách hủy bỏ dữ liệu được nhập ít mạnh mẽ hơn khi tôi bắt gặp bài đăng này. Tôi đã không đưa ra một lời chỉ trích - chỉ là một quan sát. Tôi chắc chắn đã không mong đợi để có đầu của tôi tách ra, nhưng "chào mừng bạn đến với internet" và tất cả những điều đó. * sigh * – Ozziemedes

-1

ISNULL là một nửa câu trả lời. Sử dụng NULLIF để dịch ngược về NULL. Ví dụ.

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; 

Ở đây tôi sử dụng "0Null" làm giá trị trung gian của tôi. Bạn có thể sử dụng bất cứ điều gì bạn thích. Tuy nhiên, bạn có nguy cơ va chạm với đầu vào của người dùng nếu bạn chọn một cái gì đó trong thế giới thực như "Null". Rác hoạt động tốt "! @ # 34()) 0" nhưng có thể gây nhầm lẫn hơn cho các lập trình viên trong tương lai. Tôi chắc chắn bạn sẽ có được hình ảnh.

+1

OP đang hỏi về 'unpivot', không phải' pivot'. Vui lòng sửa lại câu trả lời của bạn nếu có thể. – Conduit

+1

Nếu bạn biết bất cứ điều gì về chủ đề này .. bạn sẽ hiểu rằng giải pháp hoạt động như nhau cho UNPIVOT. Tôi sẽ không thực hiện thay đổi tầm thường cho đến khi bạn yêu cầu người khác rút lại các giải pháp của họ mà không liên quan gì đến PIVOT hoặc UNPIVOT, ví dụ: Tham gia chéo, SQL động và câu hỏi kiểm toán. Thực tế là, giải pháp của tôi là giải pháp * duy nhất * cung cấp câu trả lời cho câu hỏi ban đầu. –

+0

Câu trả lời ở đầu làm việc đủ tốt để OP được chấp nhận. Bất kể tầm thường (như một nhà phân tích làm việc với MSSQL hàng ngày, tôi nhận ra), bạn đã không trả lời được câu hỏi theo quy tắc của trang này như được định nghĩa trong [trung tâm trợ giúp] (http://stackoverflow.com/help). Bài đăng của người khác không quyết định những gì được/không được phép. Tôi không cố gắng trở thành một kẻ giật gân ở đây - tôi đã được cộng đồng đặc biệt giao nhiệm vụ xem lại bài đăng này vì đây là một trong những bài đăng đầu tiên của bạn. – Conduit

2

Tôi chạy vào cùng một vấn đề, Sử dụng CROSS ÁP DỤNG (Sql Server 2005 và sau này) Thay vì Unpivot Giải quyết vấn đề. Tôi tìm thấy giải pháp Dựa trên Điều này An Alternative (Better?) Method to UNPIVOT và tôi đã làm ví dụ sau đây để chứng minh rằng CROSS APPLY sẽ KHÔNG bỏ qua Nulls như 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 
Các vấn đề liên quan