2009-06-16 16 views
16

UNPIVOT nie zwróci wartości NULL, ale potrzebuję ich w zapytaniu porównawczym. Staram się unikać ISNULL następujący przykład (Bo w prawdziwym SQL istnieje ponad 100 dziedzinach .:Serwer SQL - dołącz NULL przy użyciu 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 

Wszelkie alternatywy?

Odpowiedz

11

To prawdziwy ból. Trzeba przełączyć je przed UNPIVOT, ponieważ nie ma rząd produkowane ISNULL() działać na - generowanie kodu jest Twój przyjaciel tutaj

mam problem na PIVOT także Brakujące wiersze przekształcić NULL, które trzeba zawinąć w ISNULL() całą drogę.. w całym rzędzie, jeśli go brakuje wartości są takie same, jak na przykład 0.0.

+0

CROSS JOIN ... CASE zachowa wartości zerowe. Zobacz poniżej przykład. –

15

Aby zachować wartości null, korzystanie 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) 

Zamiast:

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 

Edytor tekstu w trybie kolumny sprawia takie pytania łatwiej napisać. UltraEdit ma to, podobnie jak Emacs. W Emacsie nazywa się to prostokątną edycją.

Może być konieczne wykonanie skryptu dla 100 kolumn.

+0

Skryptowałbym go dla więcej niż 5 kolumn, ale jestem leniwy w ten sposób :-). Oto przykład: 'select 'select' '' + column_name + '' 'UNION ALL' from information_schema.kolumny WHERE table_name = 'table1' i table_schema = 'dbo'' – Anssssss

3

lub w SQLServer 2008 w krótszym sposób:

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

Używanie dynamicznego SQL i COALESCE, I rozwiązać problem tak:

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

Znalazłem lewy zewnętrzny łączenia wynik UNPIVOT do pełnej listy pól, wygodnie wyciągniętych z INFORMATION_SCHEMA, aby być praktyczną odpowiedzią na ten problem w niektórych kontekstach.

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

wyjście wygląda następująco:

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

Jedynym minusem tego jest to, że wszystkie twoje pola źródłowe muszą być wpisywane w sposób ciągły. – Ozziemedes

+0

@Ozziemedes Myślę, że być może brakuje ci sensu, nawet OP rzuca oryginalne dane do varchar ... w pewnym momencie w tych wszystkich technikach, oryginalne typy muszą zostać utracone z powodu fundamentalnej natury niepobudzonego powrotu, co było wcześniej różne kolumny, pod jedną kolumną. Czy na pewno używasz słowa "sąsiadujące"? Definicja tego słowa oznacza "obok siebie", a nie "ten sam", jak ci się wydaje? – Beej

+0

LOL. W kontekście: szukałem sposobu na rozpakowanie mniej mocno wpisanych danych, gdy natknąłem się na ten post. Nie robiłem krytyki - tylko obserwację. Na pewno nie spodziewałem się, że oderwę mi głowę, ale "witam w Internecie" i tak dalej. * westchnienie * – Ozziemedes

-1

ISNULL to połowa odpowiedź. Użyj NULLIF, aby tłumaczyć z powrotem na NULL. Na przykład.

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; 

Tutaj używam "0Null" jako mojej wartości pośredniej. Możesz użyć wszystkiego, co chcesz. Ryzykujesz jednak kolizję z danymi wprowadzanymi przez użytkownika, jeśli wybierzesz coś rzeczywistego, np. "Null". Śmieci działa dobrze "! @ # 34()) 0", ale może być bardziej mylące dla przyszłych programistów. Jestem pewien, że dostałeś zdjęcie.

+1

OP pyta o 'unpivot', a nie' pivot'. Popraw swoją odpowiedź, jeśli to możliwe. – Conduit

+1

Jeśli wiesz coś na ten temat, zrozumiesz, że rozwiązanie działa równie dobrze dla UNPIVOT. Nie dokonam drobnej zmiany, dopóki nie zażądasz, aby inni wycofali swoje rozwiązania, które nie mają nic wspólnego z PIVOT lub UNPIVOT, np. Cross Joins, Dynamic SQL i żarty audytorów. Faktem jest, że moje rozwiązanie jest jedynym * jedynym, które zapewnia odpowiedź na oryginalne pytanie. –

+0

Odpowiedź na górze działała na tyle dobrze, aby można było zaakceptować PO. Bez względu na trywialność (którą, jak sobie uświadamiam jako analityk pracujący z MSSQL codziennie), nie udało ci się odpowiedzieć na pytanie zgodnie z zasadami tej witryny, jak zdefiniowano w [centrum pomocy] (http: // /stackoverflow.com/help). Posty innych osób nie określają, co jest dozwolone. Nie próbuję tu być palantem - zostałem specjalnie poproszony przez społeczność o sprawdzenie tego posta, ponieważ był to jeden z twoich pierwszych. – Conduit

2

Wystąpiłam w tym samym problemie, używając aplikacji CROSS APPLY (Sql Server 2005 i nowsze) zamiast Unpivot Rozwiązał problem. Znalazłem rozwiązanie Na podstawie tego artykułu An Alternative (Better?) Method to UNPIVOT i zrobiłem następujący przykład, aby zademonstrować, że CROSS APPLY NIE Zignoruje wartości Null takich jak 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