如果该列存在,则选择columnValue,否则为null。

68
我想知道是否可以在列存在时选择该列的值,否则只选择空值。换句话说,我想要“提升”SELECT语句以处理列不存在的情况。
SELECT uniqueId
    ,  columnTwo
    ,  /*WHEN columnThree exists THEN columnThree ELSE NULL END*/ AS columnThree
FROM (subQuery) s

注意,我正在巩固我的数据模型和设计。 我希望在未来几周内排除这种逻辑,但我真的很想尽快解决这个问题,因为数据模型修复比我现在想解决的更耗时。

还要注意,我希望能够在一次查询中完成此操作。 因此,我不希望得到像以下这样的答案:

首先检查您的子查询中有哪些列。 然后修改查询以适当地处理您的子查询中的列。


1
为什么要编写假定数据模型将是随意的代码?为什么不为现有的列编写代码,当ColumnThree成为您的数据模型中的永久一等公民时,再修复查询?此外,我强烈建议不要告诉人们您不想要什么。您所述的限制是不可能满足的,您需要给出比那更好的理由。当您可以将事物封装在存储过程中时,“在一个查询中”几乎没有意义。 - Aaron Bertrand
2
@AaronBertrand 你提出了两个问题,我会一一回答。首先,我的目标是尽快发布高质量的功能。现在,为了使我的功能正常工作,我需要从ColumnThree中获取值。因此,即使解决方案不够优雅,我也会在存在该列时使用它。 - Steven Wexler
15
@AaronBertrand,其次,我感谢您的建议,但恕我不能同意。我想清楚地表达我的要求,这样用户就可以专注于提出解决我的问题的有价值的解决方案,而不会浪费时间写出我知道不会令人满意的答案。我认为明确说明哪些答案不令人满意有助于用户集中精力寻找更有价值的解决方案。 - Steven Wexler
3个回答

38

您无法使用简单的SQL语句来实现此操作。除非所有表格和列引用都存在,否则SQL查询将无法编译。

如果“子查询”是一个表格引用或视图,则可以使用动态SQL来完成此操作。

在动态SQL中,您可以执行以下操作:

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;

对于实际的子查询,您可以通过检查子查询是否返回具有该列名称的内容来近似执行相同的操作。一种方法是运行查询:select top 0 * into #temp from (<subquery>) s,然后检查#temp中的列。

编辑:

我通常不会更新这样古老的问题,但基于下面的评论。如果您对“子查询”中的每一行都有一个唯一标识符,您可以运行以下命令:

select t.. . .,  -- everything but columnthree
       (select column3   -- not qualified!
        from t t2
        where t2.pk = t.pk
       ) as column3
from t cross join
     (values (NULL)) v(columnthree);

如果外部查询中不存在 column3,子查询将会提取它。然而,这取决于每一行都有唯一标识符的关键点。问题明确是关于子查询的,我们无法保证这些行可以轻易地被唯一标识。

我不这么认为。出于好奇,我该如何使用动态SQL来实现这个? - Steven Wexler
你可以将SQL语句构建成字符串,然后使用exec命令来执行你构建的字符串。 - liebs19
根据我对动态SQL的理解,这不需要两个查询吗? - Steven Wexler
2
@Gordon +1,虽然它可以完成(请参见我的带有CROSS APPLY的答案) - ypercubeᵀᴹ
@ArthurTacca . . . 我扩展了答案。我坚持第一部分,因为问题非常普遍,有些情况下无法解决。在许多(实际)情况下,有方法可以解决这个问题。 - Gordon Linoff
显示剩余3条评论

30

正如其他人已经建议的那样,理智的方法是编写符合你表设计的查询。

不过,还有一种比较奇特的方法可以在(纯粹的,非动态的)SQL 中实现你想要的结果。类似的问题曾在 DBA.SE 上发布:如果列存在,则选择特定行,否则选择所有行的方法,但该问题更简单,因为只需将一个行和一个列作为结果。你的问题更复杂,因此查询更加复杂。以下是这种不太理智的方法:

; 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 ;
它还假定uniqueId在子查询的结果集中是唯一的。
SQL-Fiddle上测试过。
还有一种更简单的方法,它有额外的优点,可以在一个子查询中使用多个列。
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 ;

这个问题也在DBA.SE上被问到,并且已经被@Andriy M(也使用了CROSS APPLY!)和Michael Ericsson(使用XML)回答了:
为什么不能使用CASE语句来查看列是否存在并且不从中选择?


1
这很酷!有没有办法使其适用于多列,但不需要进行更多的连接? - surj
5
很聪明,利用作用域规则处理不存在的列。谢谢! - Gordon Linoff
SAP Hana是DBMS,似乎对SQL扩展支持不太多。它有大多数常规连接,但不幸的是没有CROSS/OUTER APPLY或LATERAL连接。文档在这里:http://help-legacy.sap.com/saphelp_hanaplatform/helpdata/en/20/fcf24075191014a89e9dc7b8408b26/content.htm - surj
太好了,谢谢你的提示!我会回报我的发现。 - surj
可爱的解决方案! - user732456
显示剩余6条评论

5

你可以使用动态SQL。

首先需要检查存在的,然后创建动态查询。

DECLARE @query NVARCHAR(MAX) = '
SELECT FirstColumn, SecondColumn, '+
  (CASE WHEN exists (SELECT 1 FROM syscolumns 
  WHERE name = 'ColumnName' AND id = OBJECT_ID('TableName'))
      THEN 'ColumnName'
      ELSE 'NULL as ThreeColumn'
   END) + '
FROM TableName'

EXEC sp_executesql @query;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接