为什么不能使用CASE语句查看是否存在列而不是从中选择?


17

为什么这样的事情不起作用?

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

我只是在检查列是否存在,但是,SQL Server抱怨Somecol不存在。在单个语句中是否有替代方法?


3
你有一个为什么要这么做的例子吗?我不明白为什么您要编写一个查询,尝试从可能不存在的列中进行选择。
Mark Sinkinson 2014年

4
SQL Server在执行语句之前先评估您的语句语法是否正确。因此,所有引用的列都必须存在于表中,即使它们包装在CASE语句中也是如此。
Mark Sinkinson 2014年

@MarkSinkinson:名称是语法之后检查的,但是,是的,SQL Server在实际运行批处理之前进行了检查。
Andriy M

1
从中选择INFORMATION_SCHEMA可能是一种解决方法。
Brilliand

4
@Brilliand sys.columns是更好的恕我直言。
亚伦·伯特兰

Answers:


43

下面的查询使用相同的想法,在这个惊人的答案ypercube

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

它的工作方式如下:

  • 如果dbo.Customers有名为的列SomeCol,那么SomeColin SomeCol AS MyTest将解析为dbo.Customers.SomeCol;

  • 如果表中没有这样的列,则引用将仍然有效,因为现在它将解析为dummy.SomeColdummy可以在该上下文中引用列。

您可以通过这种方式指定多个“备用”列。诀窍是不要对此类列使用表别名(在大多数情况下这是不习惯的做法,但是在这种情况下,省略表别名可以帮助您解决问题)。

如果在联接中使用该表,而另一个表有其自己的表SomeCol,则可能需要在连接中使用该查询之前,将上述查询用作派生表,以使技巧保持有效,如下所示:

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 ...
;

1
我想知道SQL编译器是否只是一点点复杂。超级酷,您可以做什么。
Max Vernon

9

一种方法是检查列是否存在,然后根据该列是否存在来构建动态SQL。

如果没有Dynamic SQL,SQL Server将在执行该语句之前尝试评估该列是否存在,从而导致错误。

但是,这确实意味着您将要编写2个查询,并且将来可能会更改。但是我不认为您应该真正针对SELECT可能不存在的列指定语句。

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

是的,但是,必须在一个语句中。最终,我正在寻找一些可能不存在的魔术系统功能。
卡森·莱因克2014年

4

您可以使用一些XML查询表中可能存在的列。

从交叉中每行的所有列构建XML,然后使用values()函数提取值。

在此查询ID中是已知的,因此直接从表中获取它。Col1和Col2可能不在,因此请使用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小提琴


-1

我的方法与其他方法仅稍有不同。我更喜欢使用系统来获取计数,因为您可以将列数分配给查询顶部的变量,然后基于该变量选择是否继续。缺点是…如果您在多个表中具有相同的列名,则不确定该列是否存在于您要查询的表中。但是,该技术也适用于特定的表,因为您只是想获得计数。

具体要求的“麻烦”是-您遇到的麻烦。通常,如果NULL值引起您的问题,请寻找另一种验证存在的方法。这是一种这样做的方法,而不会冒使服务器崩溃的风险。

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'

1
为什么不在sysobjects查询中也使用来检查特定表是否具有此类列?
ypercubeᵀᴹ

是的...我提到可以做到的...您甚至可以在要查询的特定表上执行相同的操作...我只是显示了使用COUNT的一般格式,因为当COUNT为零时COUNT不会出错,并且...我想我应该提到您也可以将其分配给变量。(例如SELECT COUNT(*)AS myVarName…)
jinzai 2014年

1
我看不出这会比Mark的查询更好。SELECT 1 ...也不出错。
ypercubeᵀᴹ

我没有说更好,但这是获得相同结果的简单得多的方法。SELECT 1可能不会出错,但它与COUNT不同。SELECT返回SOMETHING…即使它为NULL。COUNT只需要返回一个数字。这样会更快,我确实提到过该计数可以在以后使用。
jinzai 2014年

如果需要计数就可以了。但是EXISTS (SELECT ...)通常比快(SELECT COUNT(*) ...),而不是相反。
–ypercubeᵀᴹ2014年

-3

如果我正确理解...

您可以使用类似下面的查询,并根据计数进行相应的操作...如果计数> 1,则表示该表中有col,而count = 0则该表中没有col表


从INFORMATION_SCHEMA.COLUMNS中的COLUMN_NAME IN('Id')
和TABLE_SCHEMA ='dbo'和TABLE_NAME ='UserBase'中选择count(*);


4
不,您没有正确理解。还要从@AaronBertrand
Kin Shah
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.