web-dev-qa-db-fra.com

Sélectionnez columnValue si la colonne existe sinon null

Je me demande si je peux sélectionner la valeur d'une colonne si la colonne existe et sélectionner null sinon. En d'autres termes, j'aimerais "lever" l'instruction select pour gérer le cas lorsque la colonne n'existe pas.

SELECT uniqueId
    ,  columnTwo
    ,  /*WHEN columnThree exists THEN columnThree ELSE NULL END*/ AS columnThree
FROM (subQuery) s

Remarque, je suis en train de solidifier mon modèle de données et ma conception. J'espère exclure cette logique dans les semaines à venir, mais j'aimerais vraiment aller au-delà de ce problème, car la correction du modèle de données est une entreprise plus longue que je ne voudrais aborder maintenant.

Notez également que j'aimerais pouvoir le faire en une seule requête. Je ne cherche donc pas de réponse comme

vérifiez d'abord les colonnes de votre sous-requête. Modifiez ensuite votre requête pour gérer correctement les colonnes de votre sous-requête.

36
Steven Wexler

Vous ne pouvez pas le faire avec une simple instruction SQL. Une requête SQL ne se compilera que si toutes les références de table et de colonne de la table existent.

Vous pouvez le faire avec SQL dynamique si la "sous-requête" est une référence de table ou une vue.

En SQL dynamique, vous feriez quelque chose comme:

declare @sql nvarchar(max) = '
SELECT uniqueId, columnTwo, '+
    (case when exists (select *
                       from INFORMATION_SCHEMA.COLUMNS 
                       where tablename = @TableName and
                             columnname = 'ColumnThree' -- and schema name too, if you like
                      )
          then 'ColumnThree'
          else 'NULL as ColumnThree'
     end) + '
FROM (select * from '+@SourceName+' s
';

exec sp_executesql @sql;

Pour une sous-requête réelle, vous pouvez approximer la même chose en vérifiant si la sous-requête a renvoyé quelque chose avec ce nom de colonne. Une méthode consiste à exécuter la requête: select top 0 * into #temp from (<subquery>) s puis à vérifier les colonnes dans #temp.

30
Gordon Linoff

Comme d'autres l'ont déjà suggéré, l'approche saine est d'avoir des requêtes qui correspondent à la conception de votre table.

Il existe une approche plutôt exotique pour obtenir ce que vous voulez en SQL (pur, pas dynamique). Un problème similaire a été publié sur DBA.SE: Comment sélectionner des lignes spécifiques si une colonne existe ou toutes les lignes si une colonne n'existe pas mais c'était plus simple car une seule ligne et une colonne étaient voulues comme résultat . Votre problème est plus complexe, donc la requête est plus compliquée, c'est le moins qu'on puisse dire. Voici l'approche folle:

; WITH s AS
  (subquery)                                    -- subquery
SELECT uniqueId
    ,  columnTwo
    ,  columnThree =
       ( SELECT ( SELECT columnThree 
                  FROM s AS s2
                  WHERE s2.uniqueId = s.uniqueId
                ) AS columnThree
         FROM (SELECT NULL AS columnThree) AS dummy
       )
FROM s ;

Il suppose également que le uniqueId est unique dans le jeu de résultats de la sous-requête.

Testé à SQL-Fiddle


Et une méthode plus simple qui a l'avantage supplémentaire de permettre plusieurs colonnes avec une seule sous-requête:

SELECT s.*     
FROM
    ( SELECT NULL AS columnTwo,
             NULL AS columnThree,
             NULL AS columnFour
    ) AS dummy 
  CROSS APPLY
    ( SELECT 
          uniqueId,
          columnTwo,
          columnThree,
          columnFour
      FROM tableX
    ) AS s ;

La question a également été posée à DBA.SE et a été répondue par @ Andriy M (en utilisant CROSS APPLY aussi!) et Michael Ericsson (en utilisant XML):
Pourquoi ne puis-je pas utiliser une instruction CASE pour voir si une colonne existe et non SELECT à partir de celle-ci?

18
ypercubeᵀᴹ