SQL Server - Null mit Unpivot einschließen
-
05-07-2019 - |
Frage
Unpivot wird keine Nulls zurückgeben, aber ich brauche sie in einer Vergleichsabfrage. Ich versuche, das folgende Beispiel zu vermeiden (da es in der realen SQL über 100 Felder gibt .:::
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
Irgendwelche Alternativen?
Lösung
Es ist ein echter Schmerz. Sie müssen sie vor dem ausschalten UNPIVOT
, weil es keine Reihe erstellt hat für ISNULL()
Zu arbeiten - Codegenerierung ist Ihr Freund hier.
Ich habe das Problem auf PIVOT
auch. Fehlende Reihen werden zu NULL
, in was du einpacken musst ISNULL()
ganz über die Zeile, wenn fehlende Werte die gleichen sind wie 0.0
zum Beispiel.
Andere Tipps
Verwenden Sie Cross Join ... Fall: Um Nulls zu erhalten, verwenden Sie: Fall: Fall:
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)
Anstatt von:
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
Ein Texteditor mit Spaltenmodus erleichtert solche Abfragen. Ultraedit hat es auch, auch Emacs. In EMACs heißt es rechteckige Bearbeitung.
Möglicherweise müssen Sie es für 100 Spalten einstellen.
Ich bin auf das gleiche Problem gestoßen. Verwendung CROSS APPLY
(SQL Server 2005 und später) anstelle von Unpivot
Problem gelöst. Ich fand die Lösung basierend auf diesem Artikel Eine alternative (bessere?) Methode zum Lösenund ich habe das folgende Beispiel gemacht, um zu demonstrieren, dass Cross -Anwendung Nulls wie nicht ignoriert 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
Oder in SQLServer 2008 auf kürzere Weise:
...
cross join
(values('col1'), ('col2'), ('col3'), ('col4')) column_names(column_name)
Ich habe festgestellt, dass das linke äußere Ergebnis dem Unpivot -Ergebnis zu der vollständigen Liste der Felder, die bequem aus Information_Schema gezogen wurden, als praktische Antwort auf dieses Problem in einigen Kontexten sein.
-- 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>
Ausgabe sieht aus wie:
+----------------------+-----------------------+ | 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 | +----------------------+-----------------------+
Mit dynamischer SQL und Coalescecing habe ich das Problem wie dieses gelöst:
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 )
Isnull ist die halbe Antwort. Verwenden Sie NULLIF, um NULL zurückzusetzen. Z.B
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;
Hier verwende ich "0null" als meinen Zwischenwert. Sie können alles verwenden, was Sie mögen. Sie riskieren jedoch eine Kollision mit Benutzereingaben, wenn Sie etwas reales When wie "Null" auswählen. Müll funktioniert gut "!@#34 ()) 0", ist aber für zukünftige Codierer verwirrender. Ich bin sicher, Sie bekommen das Bild.