SQL Server 无法捕获链接服务器超时错误

Abo*_*azl 1 sql-server

我编写了一个通过 调用另一个过程的过程link server,它具有 try catch 块,并且 catch 块在大多数错误的情况下都可以很好地工作,例如,当我想“将 NULL 值插入到不可为空的列”时,catch 块是成功执行并将错误结果记录到我的表中,但是当涉及到时link server timeout,catch 块不起作用,我不知道我在哪里错过了导致此问题的事情。

你可以看一下我的流程

   AS
   BEGIN
   BEGIN TRY 
   BEGIN TRANSACTION
   DECLARE @BeforeCall DATETIME,@AfterCall DATETIME,@DiffSec BIGINT,@CycleId BIGINT
   DECLARE @TBL TABLE(Date INT,Time INT,Branch INT,Amount BIGINT)

   ----------------Get Last CycleId------------------------------------------
   SELECT TOP 1 @CycleId=CycleId FROM dbo.BranchesResetDate_History
   ORDER BY CycleId DESC
   SET @CycleId=ISNULL(@CycleId,0)+1;
   ------------------------------------------------------------------
   -----------------Get Reset Date--------------------------------------------
   SET @BeforeCall=GETDATE()

    INSERT INTO @TBL
    (
        Date,
        Time,
        Amount,
        Branch
    )

    EXEC [AB_TO_FK].[xxxx].dbo.GetResetDate
    
    SET @AfterCall=GETDATE()
    
    SET @DiffSec=DATEDIFF(SECOND,@BeforeCall,@AfterCall)

    -------------------------------------------------------------------
    --------------------Log Execute Result To History------------------
    INSERT INTO dbo.BranchesResetDate_History (Date,Time,Branch,Amount,InsertionDateTime,SecondsElapsed,CycleId)
    SELECT Date,Time,Branch,Amount,dbo.DateTimeMDToSHD(GETDATE()),@DiffSec,@CycleId
    FROM @TBL
    --------------------------------------------------------------------

    ---------------------Insert Result To Main Table--------------------
     INSERT INTO dbo.BranchesResetDate
     (
         ResetDate,
         ResetTime,
         BranchCode,
         Amount,
         InsertionDateTime
     )
     SELECT Date,
        Time,
        Branch,
        Amount,
        dbo.DateTimeMDToSHD(GETDATE())
        FROM @TBL
        -------------------------------------------------------------------
        -------------Delete Duplicate Records------------------------------
        DELETE res FROM (
        SELECT id,ROW_NUMBER() OVER(PARTITION BY BranchCode,ResetDate,ResetTime,Amount ORDER BY ResetTime)rn FROM dbo.BranchesResetDate
        ) res WHERE res.rn>1
        -------------------------------------------------------------------
        COMMIT TRANSACTION;

        END TRY
        BEGIN CATCH

        IF @@TRANCOUNT>0
        ROLLBACK TRANSACTION;

        ------------------------Log Exception------------------------------
        INSERT INTO dbo.Proc_Exception  (ErrorNumber,ErrorSeverity,ErrorState,ErrorProcedure,ErrorLine,ErrorMessage,DateTime)
        SELECT ERROR_NUMBER(),
               ERROR_SEVERITY(),
               ERROR_STATE(),
               ERROR_PROCEDURE(),
               ERROR_LINE(),
               ERROR_MESSAGE(),
               dbo.DateTimeMDToSHD(GETDATE())
        -------------------------------------------------------------------
        END CATCH
END
GO
Run Code Online (Sandbox Code Playgroud)

当到达下面一行时

EXEC [AB_TO_FK].[xxxx].dbo.GetResetDate
Run Code Online (Sandbox Code Playgroud)

如果出现超时错误,catch 块不会执行,我只是收到此错误

链接服务器“AB_TO_FK”的 OLE DB 提供程序“SQLNCLI11”返回消息“查询超时已过期”

Dav*_*oft 5

超时始终是由客户端(此处为具有链接服务器定义的服务器)引起的,客户端通过发送 TDS 注意事件来取消正在运行的查询。您看到的错误消息是本地生成的,并且(由于历史记录中丢失的原因)没有足够高的严重性来触发 CATCH 块或翻转@@错误。

推荐的解决方案是防止链接服务器超时。

可以通过扩展它,例如使用以下代码将远程查询超时设置为 0(无限等待):

sp_configure 'remote query timeout', 0 
go 
reconfigure with override 
go 
Run Code Online (Sandbox Code Playgroud)

根据此支持文章,或者导致远程调用失败并出现可捕获错误而不是超时。例如,您可以在程序中或在运行程序之前批量发送到远程服务器中设置 LOCK TIMEOUT 。

如果远程过程成功,则应返回 0。如果失败,则应返回某个非零值,如果由于超时而取消,则返回 null。所以你可以这样检查:

  declare @r int 
  execute @r = [AB_TO_FK].[xxxx].dbo.GetResetDate
  if (@r is null or @r <> 0)
  begin
    throw 51000, 'Linked Server Procedure failed.  Possible timeout.', 1;
  end
Run Code Online (Sandbox Code Playgroud)

您还可以捕获存储过程返回代码和结果集。如果它应该输出结果集,您还可以通过执行后检查表来确定它是否失败。例如

  declare @t table (rc int)
  declare @r int = -1
  insert into @t(id)
  execute @r = Loopback.tempdb.dbo.bar
  if (@r is null or @r <> 0 or 0 = (select count(*) from @t) )
  begin
    throw 51000, 'Linked Server Procedure failed.  Possible timeout.', 1;
  end
Run Code Online (Sandbox Code Playgroud)