【问题标题】:SQL Server: in-query error catchingSQL Server:查询中的错误捕获
【发布时间】:2017-07-18 08:23:30
【问题描述】:

在我的查询中,我必须从不受我控制的 db 中加入表。这让我发疯,因为有时无法访问此数据库(请不要问我为什么),这会破坏我的查询。我要加入的字段对我的运营来说不是基本的,我希望我的应用能够正常工作,即使这些字段一次无法访问。

这是我不拥有的数据结构:

[DBOutOfControl].[dbo].[Table1]:
[Field1]
[Field2]

[DBOutOfControl].[dbo].[Table2]:
[Field1]
[Field2]
[Field3]

这是我的桌子:

[DBInMyControl].[dbo].[Table3]:
[Field1]

我的原始查询看起来像这样:

SELECT [Table3].[MyID],
       [ForeignDataQry].[A],
       [ForeignDataQry].[B]
FROM [DBInMyControl].[dbo].[Table3]
LEFT JOIN
  (SELECT [Table1].[Field1] AS [MyID],
          [Table1].[Field2] AS [A],
          [SubQry].[Field2] AS [B]
   FROM [DBOutOfControl].[dbo].[Table1]
   LEFT JOIN
     (SELECT [Table2].[Field1],
             [Table2].[Field2]
      FROM [DBOutOfControl].[dbo].[Table2]
      WHERE [Table2].[Field3] = 'Where') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]) AS [ForeignDataQry] ON [Table3].[MyID]=[ForeignDataQry].[MyID]

我怎样才能对这个查询进行防弹,所以当 [ForeignDataQry] 生成错误时,结果将是:

[MyID]  [A]  [B]
1       NULL NULL

否则

[MyID]  [A]  [B]
1       Va1  Val2

有什么可以在服务器端做的吗?

【问题讨论】:

  • 当另一个数据库无法访问时,您的代码无法编译,因此您唯一能做的就是尝试在另一个(内部)上下文中执行它,将其包装在动态 sql 中,如果它成功什么都不做,如果没有,外部的 try..catch 会捕获它,然后你执行另一个代码而不引用不可访问的对象

标签: sql-server try-catch


【解决方案1】:

只要指定COUNT的预期结果,三个名字,就可以事先查表。轻微的重写可以允​​许它检查表以外的对象,如果需要,使用EXISTS,跳过或添加更多检查等:

IF 0 = ( -- Specify how many records you expect to come.
    SELECT COUNT(C.[name]) AS [COUNT]
    FROM sys.objects AS O
    LEFT JOIN sys.schemas AS S ON S.schema_id = O.schema_id
    LEFT JOIN sys.columns AS C ON C.object_id = O.object_id
    WHERE O.[name] = 'tablename'
      AND S.[name] = 'schemaname'
      AND C.[name] = 'columnname'
)
    SELECT 1 AS A -- Do some code.
ELSE
    SELECT 2 AS B -- Do some other code.

【讨论】:

  • 为什么sys.databases中不会有这个数据库?
  • 可以是不在线,也可以是应用程序对应的用户丢失:OP没有明确数据库无法访问的具体情况
  • @sepupic,我使用的是已弃用的吗?我只是基于参考资料,我对这些东西的参与并不多,这也让我不知道各种不可访问性。
  • 不,我不说这个,我什至没有看到你使用 sysdatabases 而不是 sys.databases。我说了另一件事,如果数据库只是在恢复、离线或在单个用户中,它仍然存在于 sys.databases 中。所以你的代码什么也解决不了
  • @sepupic,那么,他应该TRY EXEC sys.sp_executesql @statement = N'USE MOODY_DB',在通过时恢复查询,在失败时捕获和备用查询?但是如果恢复的查询是失败的部分,如何不交替呢? (当然不用布尔标志来控制)。另外,动态 SQL 不是最后的手段吗?我们真的没有其他选择吗?
【解决方案2】:

我会将有问题的查询包装在动态代码中,以便能够像这样捕获编译错误(我们无法在同一范围内捕获):

    begin try
    declare @sql varchar(4000) =

        'SELECT [Table3].[MyID],
               [ForeignDataQry].[A],
               [ForeignDataQry].[B]
        FROM [DBInMyControl].[dbo].[Table3]
        LEFT JOIN
          (SELECT [Table1].[Field1] AS [MyID],
                  [Table1].[Field2] AS [A],
                  [SubQry].[Field2] AS [B]
           FROM [DBOutOfControl].[dbo].[Table1]
           LEFT JOIN
             (SELECT [Table2].[Field1],
                     [Table2].[Field2]
              FROM [DBOutOfControl].[dbo].[Table2]
              WHERE [Table2].[Field3] = ''Where'') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]) AS [ForeignDataQry] ON [Table3].[MyID]=[ForeignDataQry].[MyID]'
       exec(@sql)
    end try
    begin catch
        SELECT [Table3].[MyID],
               cast(null as ... )as [A],
               cast(null as ...) as [B]
        FROM [DBInMyControl].[dbo].[Table3]
    end catch

这里我使用cast(null as ... )as [A] 来获得与[ForeignDataQry].[A] 相同的类型,例如如果[ForeignDataQry].[A] 是int,则应该是int:cast(null as int )as [A]

【讨论】:

    【解决方案3】:

    我以与我最初想要的不同的方式处理这个问题,但无论如何:

    我创建了一个 [Table4] 来保存外部表中的记录副本 - 字段匹配 [ForeignDataQry] + 时间戳。我创建了程序:

    CREATE PROCEDURE [dbo].[CopyForeignData] 
    AS
    
    DECLARE @Timestamp datetime
    SET @Timestamp = getdate()
    
    BEGIN
        INSERT INTO [DBInMyControl].[dbo].[Table4] ([MyID], [A], [B], [Timestamp])
        SELECT [Table1].[Field1] AS [MyID],
              [Table1].[Field2] AS [A],
              [SubQry].[Field2] AS [B],
              @Timestamp
       FROM [DBOutOfControl].[dbo].[Table1]
       LEFT JOIN
         (SELECT [Table2].[Field1],
                 [Table2].[Field2]
          FROM [DBOutOfControl].[dbo].[Table2]
          WHERE [Table2].[Field3] = 'Where') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]
    
        DELETE FROM [DBInMyControl].[dbo].[Table4] WHERE [Timestamp] <> @Timestamp
    END
    

    每当我启动我的应用程序并在那里处理错误并修改我的主 LEFT JOIN 以引用 [Table4] 时,我都会调用它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-23
      • 1970-01-01
      • 2015-04-22
      • 2023-03-11
      • 1970-01-01
      • 2018-05-19
      • 1970-01-01
      • 2019-01-23
      相关资源
      最近更新 更多