2017-08-10 39 views
3

我有大約2000 SQL更新命令運行,我知道其中一些可能會因各種原因失敗。我希望全部或分批運行它們,並捕獲那些失敗的故障,同時繼續完成列表的其餘部分。那我看更新語句 - 捕獲錯誤,並繼續與其他

有兩種方法:

XACT Abort

set xact_abort on 
begin transaction 
-- Updates here -- 
commit transaction 

Try catch

BEGIN TRY 
    -- Updates here -- 
END TRY 
BEGIN CATCH 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber, 
     ERROR_SEVERITY() AS ErrorSeverity, 
     ERROR_STATE() AS ErrorState, 
     ERROR_PROCEDURE() AS ErrorProcedure, 
     ERROR_LINE() AS ErrorLine, 
     ERROR_MESSAGE() AS ErrorMessage 
END CATCH 

,這些方法的問題是,當他們失敗了,他們失敗(至少,這是我的測試中發生的)。然後,我必須解決問題,無論哪條線路出現故障,然後重新啓動。我想忽略並記錄任何失敗,然後繼續。這可能使用xact_aborttry catch類型查詢,還是應該查看其他內容?

+2

您可以將每個更新語句包裝在try ... catch塊中。這是我在編程語言中使用循環而不是在sql server中手動執行的操作。 –

+1

如何捕獲錯誤的記錄/審計表?我想嘗試一下,但我不確定如何去做。 –

回答

1

下面是類似的東西我用的依據是:Error and Transaction Handling in SQL Server Part One (of Three) – Jumpstart Error Handling - Erland Sommarskog

表和程序設置:

create table dbo.error_handler_log (
    id int identity(1,1) not null primary key 
    , error_date datetimeoffset(7) not null 
    , severity tinyint not null 
    , [state] tinyint not null 
    , [number] int not null 
    , line int not null 
    , [procedure] sysname null 
    , message nvarchar(2048) 
); 
go 
create procedure [dbo].[error_handler_sp] as 
begin 
    set nocount, xact_abort on; 
    declare 
     @error_date datetimeoffset(7) = sysdatetimeoffset() 
    , @severity tinyint = isnull(error_severity(),16) 
    , @state tinyint = isnull(error_state(),1) 
    , @number int = isnull(error_number(),0) 
    , @line int = isnull(error_line(),0) 
    , @procedure sysname = error_procedure() 
    , @message nvarchar(2048) = error_message(); 
    insert into [dbo].[error_handler_log] 
    ([error_date],[procedure],[severity],[state],[number],[line],[message]) values 
    (@error_date, @procedure, @severity, @state, @number, @line, @message); 
    --raiserror(@message, @severity, @state); /* don't re-raise error to continue code execution */ 
end; 
go 

rextester演示:http://rextester.com/EYLAFM93158

dbfiddle.uk demo

begin try; 
    select 1/0 as err; 
end try 
begin catch; 
    exec dbo.error_handler_sp; 
end catch; 
begin try; 
    select 1/0 as err; 
end try 
begin catch; 
    exec dbo.error_handler_sp; 
end catch; 
begin try; 
    select 1/1 as one; 
end try 
begin catch; 
    exec dbo.error_handler_sp; 
end catch; 

select * 
from dbo.error_handler_log; 

回報:

+-----+ 
| one | 
+-----+ 
| 1 | 
+-----+ 

+----+----------------------------+----------+-------+--------+------+-----------+-----------------------------------+ 
| id |   error_date   | severity | state | number | line | procedure |    message    | 
+----+----------------------------+----------+-------+--------+------+-----------+-----------------------------------+ 
| 1 | 10/08/2017 13:26:00 +01:00 |  16 |  1 | 8134 | 2 | null  | Divide by zero error encountered. | 
| 2 | 10/08/2017 13:26:00 +01:00 |  16 |  1 | 8134 | 8 | null  | Divide by zero error encountered. | 
+----+----------------------------+----------+-------+--------+------+-----------+-----------------------------------+ 
+0

我測試過這個,它似乎做我需要的一切,除了它沒有將實際的查詢記錄到錯誤日誌中。你認爲這是可能的嗎?我不知道如何讓查詢進入error_handler sp。 –

+0

@ JayF1你是對的,它不記錄查詢。它確實記錄了拋出錯誤的代碼的'行號'。如果你真的想捕獲sql代碼,那麼我會說你可以把它放在一個變量中,並將其傳遞給過程以插入到表中的列中。或者,您可以傳遞一個名稱或一些代表特定代碼段的參數並將其記錄到表中。 – SqlZim

0

可能是這樣的嗎?

DECLARE @table TABLE (id int); 
DECLARE @fails TABLE (updateNumber int); 

INSERT INTO @table(id) 
VALUES (1), (2), (3); 

UPDATE @table 
SET id = id + 100 
WHERE id = 1; 

IF @@ROWCOUNT = 0 
    INSERT INTO @fails (updateNumber) VALUES (1); 

UPDATE @table 
SET id = id + 100/0 -- !!!FAIL!!! 
WHERE id = 2; 

IF @@ROWCOUNT = 0 
    INSERT INTO @fails (updateNumber) VALUES (2); 

UPDATE @table 
SET id = id + 100 
WHERE id = 3; 

IF @@ROWCOUNT = 0 
    INSERT INTO @fails (updateNumber) VALUES (3); 

SELECT * FROM @table; 
SELECT * FROM @fails; 

輸出:

id 
----------- 
101 
2 
103 

+

updateNumber 
------------ 
2 

你只需要決定什麼作爲updateNumber識別您的更新失敗。

0

採取在這個問題上偷看(由你問真正的):SQL Server XACT_ABORT with exclusion

雖然在這一問題的議程回滾一切單一故障的情況下,可以利用那裏的概念,並將邏輯應用於您的需求。

我在那裏做了30秒的解釋:對每個測試使用try/catch塊,並在所有捕獲將被引導到的過程結尾處有一個名爲「failure」的部分。您可以在每個捕獲中設置一條消息來記錄日誌,或者將錯誤傳遞給最終用戶。

對於您的需求:你會使用類似的邏輯,它與XACT_ABORT作爲的不作爲將推出一切都回到了即使是一件失敗(這是你想要的對面)。您仍然希望保留try/catch塊,並可能將日誌記錄移入每個塊中,以便繼續滾動。

保留每個CATCH塊的所有結果的日誌表,將有助於您日後分析。