TRY-CATCH에서 링크 된 서버 오류를 발견하지 못했습니다


14

연결된 서버 목록을 반복하고 각 서버에 대해 특정 쿼리를 실행하는 작업을 설정하고 있습니다. TRY-CATCH 블록 내에서 쿼리를 실행하려고하므로 특정 서버에 문제가 있으면 로깅 할 수 있지만 다른 서버로 계속 진행할 수 있습니다.

루프 내에서 실행중인 쿼리는 다음과 같습니다.

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!';

서버에 연결하는 데 문제가 있으면 코드는 즉시 실패하고 CATCH블록으로 전송되지 않습니다 . 서버가 연결되었지만 실제 쿼리에 오류 (예 : 0으로 나누기)가있는 경우 이는 CATCH블록에서 예상 한대로 파악됩니다 .

예를 들어, 존재하지 않는 이름으로 연결된 서버를 만들었습니다. 위의 내용을 실행할 때 나는 다음을 얻습니다.

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

나는 BOL을 읽었 TRY-CATCH으며 연결을 끊는 20 + 이상의 오류를 포착하지는 않지만 이것이 사실이 아니라고 생각합니다 (이것은 16 수준입니다).

왜 이러한 오류가 올바르게 잡히지 않는지 아십니까?

답변:


11

당신이 시도 할 수있는 한 가지는 사용하는 것입니다 sp_testlinkedserver 입니다. OPENQUERY런타임이 올 때까지 서버 이름의 유효성을 검사하는 구문 분석기를 지연시키기 위해 동적 SQL (Max가 올바르게 지적한대로)을 사용하여 발행 할 수도 있습니다 .

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!';

이 기능은 없이도 동일하게 작동하지만 sp_testlinkedserver해당 절차는 해당 서버에 대해 많은 코드를 시도하지 못하게하는 데 여전히 유용 할 수 있습니다 ...


또한 sp_testlinkedserver실패하면 실제로 컴파일 타임에 실패하므로 동적 SQL을 사용하여 지연시키고 여전히 캡처 할 수 있습니다.

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

6

이 같은 것을 시도 했습니까?

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;

아래 주석에 따라 오류는 더 이상 컴파일 타임에 생성되지 않기 때문에 작동합니다. sp_executesql 저장 프로 시저 내에서 런타임에 오류가 발생합니다.


감사합니다 Max. 예, 동적 SQL을 생각했습니다 (위의 내용은 실제로 올바르게 작동합니다). 그래도 오류가 발생하지 않는 이유에 관심이 있습니까?
JamesLean

@AaronBertrand PRINT 'Start';스크립트 맨 위에 단순을 추가 하면 연결에 실패하고 스크립트가 오류와 함께 종료 되더라도 출력에 인쇄됩니다. 따라서 이것은 런타임 오류가 아닌 것입니까? 내가 오해하지 않는 한?
JamesLean

가, 내가 추가했을 때 PRINT 여전히 sp_testlinkedserver대본 에서 전화를했다. 실제로 내 원본 (실패한) 스크립트를 사용하여 인쇄되지 않습니다. 따라서 이것이 실제로 컴파일 타임 오류 인 것처럼 보이므로 이것이 잡히지 않는 이유입니다.
JamesLean

@JamesLean 너무 웃겨, 당신이 제안하는 것을 확인하기 위해 재현에 갔을 때, 나는 sp_testlinkedserver전화를 주석 처리 했지만 SELECT동적 SQL로 남겨 두었습니다 . (가) PRINT직접 서버 이름을 참조하는 경우 앞서 제안했다 그래서 같이 발생하지 않습니다, BEGIN TRY오류가 처음 발생하기 때문에 입력하지 않습니다.
Aaron Bertrand

4

조사 후 런타임 오류가 아니라 컴파일 타임 오류이므로이 오류가 발생하지 않는 것 같습니다. 이를 입증하려면 다음을 시도하십시오.

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;

초기 PRINT명령문은 출력을 얻지 못하고 0으로 나누기 오류가 실행되거나 잡히지 않습니다. 존재하지 않는 서버는 스크립트를 즉시 실패시킵니다.


3

최근에 TRY-CATCH 내에서 원격 프로 시저를 호출했을 때 비슷한 문제가 발생하여 중복 키를 삽입하려고 시도하여 프로 시저가 실패했습니다 (레벨 16 런타임 오류). CATCH 블록이 호출되지 않았습니다. 이 기사에서 이유를 찾았습니다. https://technet.microsoft.com/en-us/library/ms191515(v=sql.105).aspx

해결책은 원격 프로 시저를 호출하기 전에 호출 프로 시저에서 XACT_ABORT ON을 설정하는 것입니다. XACT_ABORT가 있으면 CATCH 블록이 예상대로 호출됩니다. XACT_ABORT 설정이 원격 프로 시저에 전파되어 해당 동작에 영향을 줄 수 있음을 알아야합니다.


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

1
안녕하세요, 우선 사이트에 오신 것을 환영합니다. 여기 주위에 우리는 추가 정보가없는 코드 벽이 아닌 작동 방식에 대한 약간의 설명을 좋아합니다. 그러나 답변 주셔서 감사합니다.
Tom V-topanswers.xyz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.