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?

Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top