捕捉錯誤 - Amazon Redshift

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

捕捉錯誤

當預存程序中的查詢或命令造成錯誤時,後續查詢不會執行,而且會復原交易。但您可以使用 EXCEPTION 區塊來處理錯誤。

注意

預設行為是,即使預存程序中沒有其他產生錯誤的條件,錯誤也會導致後續查詢無法執行。

[ <<label>> ] [ DECLARE declarations ] BEGIN statements EXCEPTION WHEN OTHERS THEN statements END;

當發生例外狀況,而您新增例外狀況處理區塊時,您可以撰寫 RAISE 陳述式和大多數其他 PL/pgSQL 陳述式。例如,您可以使用自訂訊息引發例外狀況,或將記錄插入記錄資料表。

進入例外狀況處理區塊時,會復原目前的交易,並建立新的交易以執行區塊中的陳述式。如果區塊中的陳述式沒有錯誤地執行,就會遞交交易並重新擲回例外狀況。最後,預存程序會結束。

例外狀況區塊中唯一支援的條件是 OTHERS,可比對各種錯誤類型 (查詢取消除外)。此外,如果例外狀況處理塊中發生錯誤,則外部例外狀況處理塊可以捕獲該錯誤。

當 NONATOMIC 程序內部發生錯誤時,如果錯誤是由例外狀況區塊處理,則不會重新擲回錯誤。請參閱 PL/pgSQL 陳述式 RAISE 以擲出例外狀況處理區塊捕獲的例外狀況。此陳述式僅在例外狀況處理區塊中有效。如需更多資訊,請參閱 RAISE

使用 CONTINUE 處理常式控制預存程序中發生錯誤之後的情況

CONTINUE 處理常式是一種例外狀況處理常式,可控制 NONATOMIC 預存程序內的執行流程。透過使用的過程,您可以揪出問題點並處理異常,而不需結束現有陳述式區塊。通常當預存程序中發生錯誤時,流程會中斷,並將錯誤傳回給呼叫者。但是,在某些使用案例中,錯誤情況還沒有嚴重到足以中斷流程。您可能想要按既定程序處理錯誤,使用您在不同的交易中選擇的錯誤處理邏輯,然後繼續執行錯誤後續的陳述式。語法如下列所示。

[ DECLARE declarations ] BEGIN statements EXCEPTION [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN handler_statements END;

有數個系統資料表可協助您收集有關各種錯誤類型的資訊。如需詳細資訊,請參閱 STL_LOAD_ERRORSSTL_ERRORSYS_STREAM_SCAN_ERRORS。您也可以使用其他系統資料表來疑難排解錯誤。有關這些的更多資訊,請參閱系統資料表和檢視參考

範例

下列範例說明如何在例外狀況處理區塊中撰寫陳述式。預存程序正在使用預設的交易管理行為。

CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); CREATE OR REPLACE PROCEDURE update_employee_sp() AS $$ BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; $$ LANGUAGE plpgsql; CALL update_employee_sp(); INFO: An exception occurred. ERROR: column "invalid" does not exist CONTEXT: SQL statement "select invalid" PL/pgSQL function "update_employee_sp" line 3 at execute statement

在這個範例中,如果我們呼叫 update_employee_sp,會引發資訊訊息「發生例外狀況。」並將錯誤訊息插入記錄資料表的 employee_error_log 日誌中。在預存程序結束之前,會再次擲回原始例外狀況。下列查詢顯示執行範例所產生的記錄。

SELECT * from employee; firstname | lastname -----------+---------- Tomas | Smith SELECT * from employee_error_log; message ------------------------------------------------ Error message: column "invalid" does not exist

如需 RAISE 的詳細資訊,包括格式化說明和其他層級清單,請參閱支援的 PL/pgSQL 陳述式

下列範例說明如何在例外狀況處理區塊中撰寫陳述式。預存程序正在使用 NONATOMIC 交易管理行為。在此範例中,程序呼叫完成後,不會擲回呼叫者的錯誤。UPDATE 陳述式不會因為下一個陳述式中的錯誤而復原。會引發資訊訊息,並在記錄資料表中插入錯誤訊息。

CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); -- Create the SP in NONATOMIC mode CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS $$ BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; $$ LANGUAGE plpgsql; CALL update_employee_sp_2(); INFO: An exception occurred. CALL SELECT * from employee; firstname | lastname -----------+---------- Adam | Smith (1 row) SELECT * from employee_error_log; message ------------------------------------------------ Error message: column "invalid" does not exist (1 row)

此範例示範如何使用兩個子區塊建立程序。呼叫預存程序時,第一個子區塊的錯誤會由其例外狀況處理區塊處理。在第一個子區塊完成之後,程序會繼續執行第二個子區塊。從結果可以看到,程序呼叫完成時沒有擲回任何錯誤。資料表 employee 上的 UPDATE 和 INSERT 操作已遞交。來自兩個例外狀況區塊的錯誤訊息都會插入記錄資料表中。

CREATE TABLE employee (firstname varchar, lastname varchar); INSERT INTO employee VALUES ('Tomas','Smith'); CREATE TABLE employee_error_log (message varchar); CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS $$ BEGIN BEGIN UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith'; EXECUTE 'select invalid1'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred in the first block.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; BEGIN INSERT INTO employee VALUES ('Edie','Robertson'); EXECUTE 'select invalid2'; EXCEPTION WHEN OTHERS THEN RAISE INFO 'An exception occurred in the second block.'; INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM); END; END; $$ LANGUAGE plpgsql; CALL update_employee_sp_3(); INFO: An exception occurred in the first block. INFO: An exception occurred in the second block. CALL SELECT * from employee; firstname | lastname -----------+----------- Adam | Smith Edie | Robertson (2 rows) SELECT * from employee_error_log; message ------------------------------------------------- Error message: column "invalid1" does not exist Error message: column "invalid2" does not exist (2 rows)

下列範例示範如何使用 CONTINUE 例外狀況處理常式。此範例會建立兩份資料表,並在預存程序中使用資料表。CONTINUE 處理常式會以 NONATOMIC 交易管理行為控制預存程序中的執行流程。

CREATE TABLE tbl_1 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS $$ BEGIN INSERT INTO tbl_1 VALUES (1); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (2); EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;

呼叫預存程序:

CALL sp_exc_handling_1();

流程如下所示:

  1. 因為嘗試在資料欄插入不相容的資料類型而發生錯誤。控制傳遞給 EXCEPTION 區塊。當進入例外狀況處理區塊時,會復原目前的交易,並建立新的隱含交易以執行區塊中的陳述式。

  2. 如果 CONTINUE_HANDLER 中的陳述式執行無錯誤,則控制權會傳遞至立即接續在導致例外狀況的陳述式之後的陳述式。(如果 CONTINUE_HANDLER 中的陳述式引發新的異常,則可以使用 EXCEPTION 區塊中的處理程序來處理它。)

呼叫範例預存程序之後,資料表包含下列記錄:

  • 如果執行 SELECT * FROM tbl_1;,它會傳回兩個記錄。這些包含值 12

  • 如果執行 SELECT * FROM tbl_error_logging;,它會傳回一個包含下列值的記錄:發生的錯誤42703tbl_1 中不存在資料欄 "val"

下列其他錯誤處理範例會同時使用 EXIT 處理常式和 CONTINUE 處理常式。此範例建立兩個表:一個資料表和一個記錄表。另外還建立一個展示如何處理錯誤的預存程序:

CREATE TABLE tbl_1 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS $$ BEGIN INSERT INTO tbl_1 VALUES (1); BEGIN INSERT INTO tbl_1 VALUES (100); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (101); EXCEPTION EXIT_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; INSERT INTO tbl_1 VALUES (2); -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_1 VALUES ("val"); INSERT INTO tbl_1 VALUES (3); EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;

建立預存程序後,使用下列命令呼叫:

CALL sp_exc_handling_2();

當內部例外區塊 (由 BEGIN 和 END 的內部集合括住)中發生錯誤時,會由 EXIT 處理常式處理。外部區塊中發生的任何錯誤則由 CONTINUE 處理常式處理。

呼叫範例預存程序之後,資料表包含下列記錄:

  • 如果執行 SELECT * FROM tbl_1;,它會傳回四個記錄,其值為 1、2、3 和 100。

  • 如果執行 SELECT * FROM tbl_error_logging;,它會傳回兩個記錄。它們具有以下值:發生的錯誤42703tbl_1 中不存在資料欄 "val"

如果表 tbl_error_logging 不存在,則會引發例外狀況。

下列範例展示如何使用 CONTINUE 例外狀況處理常式與 FOR 迴圈。此範例會建立三個資料表,並在預存程序內的 FOR 迴圈中使用這些資料表。FOR 迴圈是結果集變體,這要示它會反覆運算查詢結果:

CREATE TABLE tbl_1 (a int); INSERT INTO tbl_1 VALUES (1), (2), (3); CREATE TABLE tbl_2 (a int); CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar); CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS $$ DECLARE rec RECORD; BEGIN FOR rec IN SELECT a FROM tbl_1 LOOP IF rec.a = 2 THEN -- Expect an error for the insert statement following, because of the invalid value INSERT INTO tbl_2 VALUES("val"); ELSE INSERT INTO tbl_2 VALUES (rec.a); END IF; END LOOP; EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM); END; $$ LANGUAGE plpgsql;

呼叫預存程序:

CALL sp_exc_handling_loop();

呼叫範例預存程序之後,資料表包含下列記錄:

  • 如果執行 SELECT * FROM tbl_2;,它會傳回兩個記錄。這些包含值 1 和 3。

  • 如果執行 SELECT * FROM tbl_error_logging;,它會傳回一個包含下列值的記錄:發生的錯誤42703tbl_2 中不存在資料欄 "val"

有關 CONTINUE 處理常式的使用須知:

  • CONTINUE_HANDLER 和 EXIT_HANDLER 關鍵字只能在 NONATOMIC 預存程序中使用。

  • CONTINUE_HANDLER 和 EXIT_HANDLER 關鍵字是選用的。EXIT_HANDLER 是預設值。