从子查询中的丢失表错误中进行选择(在CASE中存在后)

时间:2019-03-25 17:33:08

标签: sql sql-server

** 已编辑为在EXISTS测试中反转逻辑

我需要从可能不存在的表中选择一个字段。我还需要在子查询中执行此操作。

有一个来自工程的代码发行版,它将向我抓取的数据库中添加表以进行报告。如果该表存在,请选择该字段。如果没有,则给出一般故障。

**是的,有很多关于如何使用EXISTS的示例。这超出了它们的范围,因为它处理了可能不存在的问题。

新版本的代码将推广到多个站点,某些站点可能没有其他站点的表。我的东西必须为第0天做好准备-又名:提前推出(第-1天?)。

这是我要在SQL Server Management Studio(SQL 2016)(SSMS 17.4)中工作的代码(表和列重命名)

select 'Cern' as  SiteName, 
(
select 
case when exists (select 1 from sys.tables where name like 'ProtonAcceleratorSecurity')
 then
   (select top 1 isnull(SecurityID,'Not on file') from [ProtonAcceleratorSecurity] with (nolock) )  
 else
   (select 'No Security Table Found')
end  
 as SecurityID 

它看起来像是人为的,但它与我要使用的确切查询非常接近(但与安全性无关)

目标是取回站点名称和第一个安全性ID(随机-无关紧要-不需要一致性)-但前提是该表确实存在。

问题是SSMS抛出一个错误,告诉我该表是我已经知道的无效对象。

“无效的对象名称'ProtonAcceleratorSecurity'”

@TabAlleman的最终答案,并作了小幅修改:


SET @sql = '
select ''Cern'' as  SiteName, ' +
( select 
    case when exists (select 1 from sys.tables where name like 'ProtonAcceleratorSecurity')
 then
   '(select top 1 isnull(SecurityID,''Not on file'') from [ProtonAcceleratorSecurity] with (nolock) )'
 else
   '''No Security Table Found'''
end  ) 
+ ' as SecurityID ';

EXEC (@sql); ```

3 个答案:

答案 0 :(得分:2)

发生错误的原因是SQL Server将在执行之前分析并编译整个语句。因此,即使查询本身在引用对象之前进行检查以确保对象是有效的,您的查询也不能包含对无效对象的引用。解析器不够聪明,无法知道如果对象不存在则不会调用子查询,因此它将完全阻止您运行查询。

您可以欺骗解析器的一种方法是通过动态sql,它不会被预先解析:

DECLARE @sql varchar(max);

SET @sql = '
select ''Cern'' as  SiteName, '

+
select 
case when exists (select 1 from sys.tables where name like 'ProtonAcceleratorSecurity')
 then
   '(select top 1 isnull(SecurityID,''Not on file'') from [ProtonAcceleratorSecurity] with (nolock) )'
 else
   '''No Security Table Found'''
end  
+

' as SecurityID 
';

EXEC (@sql);

编辑是因为我不是在进行测试,而是为了阐明主意:

您需要检查外部查询中是否存在该表,但是仅构造一个使用该表的动态查询(如果存在)。要使其变得非常简单,忽略原始的CASE结构,您需要在逻辑上做到这一点:

IF EXISTS({SELECT query to test for MyTable})
  @SQL = 'query that references MyTable';
ELSE
  @SQL = 'query that doesn't reference MyTable';

EXECUTE (@SQL);

PS:我想我只是在第一个示例中修复了语法。

答案 1 :(得分:0)

我认为在单个查询中没有任何方法可以做到这一点。一种建议是将其隐藏在视图中:

IF OBJECT_ID('ProtonAcceleratorSecurity') IS NULL
BEGIN
    CREATE VIEW ProtonAcceleratorSecurity
        SELECT 'Not on file' as SecurityID
END;

然后您的查询可以简单地工作为:

select 'Cern' as SiteName, 
       (select top 1 coalesce(SecurityID, 'Not on file')
        from ProtonAcceleratorSecurity 
       ) as SecurityID 

答案 2 :(得分:0)

您遇到了问题,因为执行语句时表必须存在。

如果将其分为两个语句,则没有问题。引用不存在的表的那个被标记为延迟编译,并且因为它永远不会执行,所以完全不需要编译并且没有错误。

DECLARE @table NVARCHAR(255)
DECLARE @column NVARCHAR(255)

DECLARE OUTER_CURSOR CURSOR 
FOR SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO

OPEN OUTER_CURSOR 

FETCH NEXT FROM OUTER_CURSOR INTO @table
WHILE (@@FETCH_STATUS <> -1)
BEGIN

        DECLARE INNER_CURSOR CURSOR 
        FOR SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO

        OPEN INNER_CURSOR 

        FETCH NEXT FROM INNER_CURSOR INTO @column

        WHILE (@@FETCH_STATUS <> -1)
        BEGIN


            DECLARE @strQuery NVARCHAR(MAX) 
            SET @strQuery = 'UPDATE [' + @table + '] SET [' + @column + '] = ''101211'' WHERE [' + @column + '] = ''10120'';'

            EXEC(@strQUERY)

            FETCH NEXT FROM INNER_CURSOR INTO @column

        END

        CLOSE INNER_CURSOR
        DEALLOCATE INNER_CURSOR

FETCH NEXT FROM OUTER_CURSOR INTO @table

END

CLOSE OUTER_CURSOR
DEALLOCATE OUTER_CURSOR