it-swarm-eu.dev

Chyba propojeného serveru nebyla zachycena pomocí TRY-CATCH

Vytvářím úlohu tak, aby procházela seznamem propojených serverů a provedla konkrétní dotaz proti každému z nich. Snažím se provést dotaz uvnitř bloku TRY-CATCH, takže pokud existuje problém s jedním konkrétním serverem, mohu se přihlásit, ale pak pokračovat s ostatními servery.

Dotaz, který provádím uvnitř smyčky, vypadá asi takto:

BEGIN TRY
    SELECT *
    FROM OPENQUERY([server1], 'SELECT 1 AS c;');
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER(), ERROR_MESSAGE();
END CATCH;

PRINT 'We got past the Catch block!';

Pokud se vyskytne problém s připojením k serveru, kód se okamžitě nezdaří a nepřenáší se do bloku CATCH. Pokud se server připojí, ale ve skutečném dotazu je chyba, např. vydělte nulou, pak je to zachyceno podle očekávání blokem CATCH.

Například jsem vytvořil propojený server se jménem, ​​o kterém vím, že neexistuje. Když provedu výše uvedené, dostanu:

OLE DB provider "SQLNCLI" for linked server "nonserver" returned message 
    "Login timeout expired".
OLE DB provider "SQLNCLI" for linked server "nonserver" returned message 
    "An error has occurred while establishing a connection to the server. 
    When connecting to SQL Server 2005, this failure may be caused by the 
    fact that under the default settings SQL Server does not allow remote
    connections.".
Msg 53, Level 16, State 1, Line 0
Named Pipes Provider: Could not open a connection to SQL Server [53].

Četl jsem BOL na TRY-CATCH a víme, že nezachytí chyby úrovně 20+, které přeruší připojení, ale zdá se, že tomu tak není (toto je pouze úroveň 16).

Ví někdo, proč tyto chyby nejsou zachyceny správně?

14
JamesLean

Jedna věc, kterou můžete zkusit, je použít sp_testlinkedserver . Můžete také vydat OPENQUERY pomocí dynamického SQL (jak Max správně správně poukázal), abyste odložili syntaktický analyzátor, který ověří název serveru, na běhový modul.

BEGIN TRY
    EXEC sp_testlinkedserver N'server1';

    EXEC sp_executesql N'SELECT * FROM OPENQUERY([server1], 
      ''SELECT 1 AS c;'');';
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER(), ERROR_MESSAGE();
END CATCH;

PRINT 'We got past the Catch block!';

I když to funguje stejně dobře bez sp_testlinkedserver, tato procedura může být stále užitečná v tom, že vám brání vyzkoušet spoustu kódu proti tomuto serveru ...


Také, protože pokud sp_testlinkedserver selže, ve skutečnosti selže v době kompilace, můžete to odložit a stále jej také zachytit pomocí dynamického SQL:

BEGIN TRY
  EXEC master.sys.sp_executesql N'EXEC sp_testlinkedserver N''server1'';';
  ...
END TRY
11
Aaron Bertrand

Vyzkoušeli jste něco takového?

BEGIN TRY
    DECLARE @cmd nvarchar(max);
    SET @cmd = 'SELECT * FROM OPENQUERY([server1], ''SELECT 1 AS c;'');';
    EXEC sp_executesql @cmd;
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER(), ERROR_MESSAGE();
END CATCH;

Podle komentářů níže to funguje, protože chyba již není generována v době kompilace. K chybě nyní dochází za běhu uvnitř uložené procedury sp_executesql.

6
Max Vernon

Po prozkoumání to vypadá, že tato chyba není zachycena, protože se jedná spíše o chybu compile-time, než o chybu runtime. Chcete-li to prokázat, zkuste následující:

PRINT 'Before TRY';

BEGIN TRY
    SELECT 1/0;

    SELECT *
    FROM OPENQUERY([nonserver], 'SELECT 1 AS c;');
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER(), ERROR_MESSAGE();
END CATCH;

Počáteční příkaz PRINT nedostane výstup a ani se nevydá dělení nulovou chybou. Neexistující server způsobí okamžité selhání skriptu.

4
JamesLean

Nedávno jsem měl podobný problém, když jsem volal vzdálenou proceduru zv rámci TRY-CATCH a procedura selhala kvůli pokusu o vložení duplicitní klávesy (chyba 16 runtime time). Blok CATCH nebyl vyvolán. Důvod jsem našel v tomto článku: https://technet.Microsoft.com/en-us/library/ms191515 (v = sql.105) .aspx

Řešením je SET XACT_ABORT ON ve volací proceduře před vyvoláním vzdálené procedury. Když je XACT_ABORT v CATCH bloku, vyvolá se podle očekávání. Musíte si uvědomit, že nastavení XACT_ABORT je rozšířeno do vzdálené procedury a může to ovlivnit jeho chování.

3
R.M. Buda
ALTER PROCEDURE dbo.LinkedServer_Status 
    @linked_server nvarchar(128),
    @exists bit OUT,
    @connected bit OUT,
    @server_datetime datetime OUT
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @server_id int;
    SELECT @server_id = server_id from sys.servers where name = @linked_server;
    IF (@@ROWCOUNT = 0)
        SELECT @exists = 0, @connected = 0, @server_datetime = null;
    ELSE BEGIN
        SELECT @exists = 1;
        BEGIN TRY
            DECLARE @TBL TABLE(server_datetime DateTime);
            DECLARE @SQL nVarChar(2048); -- MUST BE nVarChar
            SELECT @SQL =
                'SELECT server_datetime FROM OPENQUERY(['+RTRIM(@linked_server)+'], ''SELECT GETDATE() server_datetime'')'; 
            INSERT @TBL EXEC sp_executesql @SQL;
            SELECT TOP 1 @connected = 1, @server_datetime = server_datetime FROM @TBL;
        END TRY
        BEGIN CATCH
            SELECT @connected = 0, @server_datetime = null;
            SELECT ERROR_MESSAGE();
        END CATCH
    END;
END

-- now use stored procedure

SET NOCOUNT ON;

DECLARE
    @linked_server nvarchar(128),
    @exists bit,
    @connected bit,
    @server_datetime datetime

SELECT @linked_server = 'FRICKE BMS';

exec dbo.LinkedServer_Status
    @linked_server, 
    @exists OUT, 
    @connected OUT, 
    @server_datetime OUT;

IF (@exists = 0)
    PRINT 'Linked Server "' + @linked_server + '" DOES NOT Exist';
ELSE BEGIN
    PRINT 'Linked Server "' + @linked_server + '" Exists';
    IF (@connected = 0)
        PRINT 'Linked Server "' + @linked_server + '" NOT Connected';
    ELSE
        PRINT 'Linked Server "' + @linked_server + '" IS Connected; Server DateTime: '+convert(varchar(25), @server_datetime, 120) 
END;
0
Jeffrey W Lott