SQL Server - Inclure la valeur NULL à l'aide de UNPIVOT
-
05-07-2019 - |
Question
UNPIVOT ne renverra pas les valeurs NULL, mais j'en ai besoin dans une requête de comparaison. J'essaie d'éviter d'utiliser ISNULL dans l'exemple suivant (car dans le vrai SQL, il y a plus de 100 champs.:
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
Des alternatives?
La solution
C'est une vraie douleur. Vous devez les désactiver avant le UNPIVOT
, car aucune ligne n'est produite pour que ISNULL ()
fonctionne - la génération de code est votre ami ici.
J'ai également un problème avec PIVOT
. Les lignes manquantes deviennent NULL
, que vous devez insérer dans ISNULL ()
sur toute la ligne si les valeurs manquantes sont identiques à 0.0
. par exemple.
Autres conseils
Pour conserver les valeurs NULL, utilisez 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)
Au lieu de:
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
Un éditeur de texte avec le mode colonne facilite l'écriture de telles requêtes. UltraEdit l'a, tout comme Emacs. Dans Emacs, cela s’appelle édition rectangulaire.
Vous devrez peut-être créer un script pour 100 colonnes.
J'ai rencontré le même problème. L'utilisation de CROSS APPLY
(SQL Server 2005 et versions ultérieures) au lieu de Unpivot
a résolu le problème. J'ai trouvé la solution basée sur cet article Une méthode alternative (meilleure?) à UNPIVOT
et j'ai créé l'exemple suivant pour démontrer que CROSS APPLY n'ignorera PAS les valeurs NULL telles que 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
ou, dans SQLServer 2008, de manière plus courte:
...
cross join
(values('col1'), ('col2'), ('col3'), ('col4')) column_names(column_name)
J'ai constaté que joindre l'extérieur du résultat UNPIVOT à la liste complète des champs, tiré commodément de INFORMATION_SCHEMA, était une réponse pratique à ce problème dans certains contextes.
-- 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>
La sortie ressemble à:
+----------------------+-----------------------+ | 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 | +----------------------+-----------------------+
À l'aide de SQL dynamique et de COALESCE, j'ai résolu le problème de la manière suivante:
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 est la moitié de la réponse. Utilisez NULLIF pour traduire en NULL. Par exemple.
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;
Ici, j'utilise " 0Null " comme ma valeur intermédiaire. Vous pouvez utiliser ce que vous voulez. Cependant, vous risquez une collision avec les entrées de l'utilisateur si vous choisissez quelque chose du monde réel tel que "Null". Les ordures fonctionnent bien "!! @ # 34 ()) 0 " mais peut être plus déroutant pour les futurs codeurs. Je suis sûr que vous obtenez l'image.