web-dev-qa-db-fra.com

Pourquoi ne puis-je pas utiliser une instruction CASE pour voir si une colonne existe et non SELECT à partir de celle-ci?

Pourquoi quelque chose comme ça ne marche pas?

SELECT
CASE 
WHEN NULLIF(COL_LENGTH('Customers', 'Somecol'), '') IS NULL THEN NULL
ELSE Somecol
END AS MyTest
FROM Customers;

Je vérifie simplement si la colonne existe, cependant, SQL Server se plaint que Somecol n'existe pas. Y a-t-il une alternative à cela dans une seule déclaration?

17
Carson Reinke

Le requête suivante utilise la même idée que dans cette réponse étonnante par ypercube :

SELECT x.*
FROM (SELECT NULL AS SomeCol) AS dummy
CROSS APPLY
(
  SELECT
    ID,
    SomeCol AS MyTest
  FROM dbo.Customers
) AS x;

Cela fonctionne comme ceci:

  • si dbo.Customers a une colonne nommée SomeCol, puis SomeCol dans SomeCol AS MyTest sera résolu comme dbo.Customers.SomeCol;

  • si la table n'a pas une telle colonne, la référence sera toujours valide, car maintenant elle sera résolue comme dummy.SomeCol: dummy les colonnes peuvent être référencées dans ce contexte.

Vous pouvez spécifier plusieurs colonnes "de rechange" de cette façon. L'astuce n'est pas d'utiliser l'alias de table pour de telles colonnes (ce qui est une pratique désapprouvée dans la plupart des situations, mais dans ce cas, l'omission de l'alias de table vous aide à résoudre le problème).

Si la table est utilisée dans une jointure et que l'autre table a son propre SomeCol, vous devrez probablement utiliser la requête ci-dessus en tant que table dérivée avant de l'utiliser dans la jointure afin de continuer à fonctionner, quelque chose comme ça:

SELECT ...
FROM
(
  SELECT x.*
  FROM (SELECT NULL AS SomeCol) AS dummy
  CROSS APPLY (
    SELECT
      ID,
      SomeCol AS MyTest
    FROM dbo.Customers
  ) AS x
) AS cust
INNER JOIN ...
;
44
Andriy M

Une façon de procéder consiste à vérifier l'existence des colonnes, puis à créer le Dynamic SQL en fonction de l'existence ou non de cette colonne.

Sans Dynamic SQL, SQL Server tentera d'évaluer si la colonne existe ou non avant même d'exécuter l'instruction, ce qui entraîne une erreur.

Cela signifie cependant que vous aurez 2 requêtes à écrire et potentiellement modifier à l'avenir. Mais je ne pense pas que vous devriez vraiment cibler les instructions SELECT sur des colonnes qui peuvent ne pas exister.

declare @SQL varchar(max)

If exists (select 1 from sys.columns where Name = N'NameOfColumn' and object_id=object_id(N'yourTableName'))
begin
set @SQL = 'select ID, NameOfColumn from yourTableName'
exec(@sql)
end
else
begin
Print 'Column does not exist'
end
9
Mark Sinkinson

Vous pouvez utiliser du XML pour interroger les colonnes qui pourraient être dans le tableau.

Créez un XML à partir de toutes les colonnes par ligne dans une application croisée et extrayez la valeur à l'aide de la fonction values().

Dans cette requête, l'ID est connu, récupérez-le directement dans la table. Col1 et Col2 peuvent être là ou pas, alors utilisez-les en XML.

select T.ID,
       TX.X.value('(Col1/text())[1]', 'int') as Col1,
       TX.X.value('(Col2/text())[1]', 'int') as Col2
from T
  cross apply (select T.* for xml path(''), type) as TX(X)

SQL Fiddle

4
Mikael Eriksson