EXCEPTION (Snowflake Scripting)¶
指定如何处理 Snowflake Scripting 块中引发的异常。
有关异常的详细信息,请参阅 处理异常。
- 另请参阅:
语法¶
EXCEPTION
WHEN <exception_name> [ OR <exception_name> ... ] [ { EXIT | CONTINUE } ] THEN
<statement>;
[ <statement>; ... ]
[ WHEN ... ]
[ WHEN OTHER [ { EXIT | CONTINUE } ] THEN ]
<statement>;
[ <statement>; ... ]
其中:
exception_name在 当前块的 DECLARE 部分 或封闭块中定义的异常名称。
使用说明¶
每个 块 都可以有自己的异常处理程序。
Snowflake 支持每个块不超过一个异常处理程序。但是,通过使用多个
WHEN子句,该处理程序可以捕获多种类型的异常。WHEN OTHER [ { EXIT | CONTINUE } ] THEN子句捕获任何尚未指定的异常。异常处理程序适用于声明它的块中 BEGIN 与 EXCEPTION 部分之间的语句。它不适用于该块的 DECLARE 部分。
仅当指定的异常在 范围 内时,异常处理程序才能处理该指定的异常。
如果存储过程旨在返回值,则它应从每个可能的退出路径(包括异常处理程序中
EXIT类型的 每个WHEN子句)返回一个值。若要在异常处理程序中使用变量,该变量必须在 DECLARE 部分或作为实参传递给存储过程。该变量不能在 BEGIN ...END 部分声明。有关更多信息,请参阅 在 Snowflake Scripting 中将变量传递给异常处理程序。
发生异常时,系统将按顺序检查处理程序条件并使用首个匹配的
WHEN子句。在同一块内的顺序是从上到下,且会先检查内部块,然后再检查外部块。系统不会优先选择EXIT或CONTINUE处理程序,哪个先匹配就执行哪个。每条语句仅能匹配一个处理程序。但是,在异常处理程序主体内部遇到的所有异常都可以触发外部块异常处理程序。
在异常处理程序中,每个
WHEN子句可以是以下类型之一:EXIT– 块会执行处理程序中的语句,然后退出当前块。如果块运行此类型的异常,并且块在异常处理程序之后包含语句,则系统不会运行这些语句。如果块是内部块,并且异常处理程序不包含
RETURN语句,则执行将退出内部块并继续执行外部块中的代码。EXIT是默认值。CONTINUE- 块执行处理程序中的语句,并继续执行紧随导致错误的语句之后的语句。
一个
EXCEPTION子句可以同时包含EXIT和CONTINUE类型的WHEN子句。对于
CONTINUE类型的WHEN子句,以下使用说明适用:如果错误在 分支结构 中引发,则继续执行的语句是紧接在分支结构之后的语句。
如果错误在 循环 的条件中引发,则继续执行的语句是紧接在循环之后的语句。
如果错误在循环主体中引发,则继续执行的语句是循环的下一次迭代中的语句。有关示例,请参阅 处理异常并继续。
如果错误在 RETURN 语句中引发,则继续执行的语句是紧接在
RETURN语句之后的语句。如果错误在 嵌套存储过程 中引发,并且错误由外部作用域处理,则继续执行的语句是紧接在存储过程调用之后的语句。
请避免在
CONTINUE类型的WHEN子句中包含RETURN语句。如果包含RETURN语句,则存储过程会直接返回,而不会继续执行。
对于
CONTINUE类型的WHEN子句,以下示例显示了在不同情况下,紧接在导致错误的语句之后的语句是哪一条。在这些示例中,error_expression是引发异常的表达式,continue_statement是执行完CONTINUE处理程序语句之后,代码在块中继续执行的语句。DECLARE ... BEGIN ... LET a := <error_expression>; <continue_statement>; ... EXCEPTION WHEN <exception_name> CONTINUE THEN ... END;
LET x := <valid_expression>; x := <error_expression>; <continue_statement>
SELECT <statement> INTO <error_expression>; <continue_statement>;
IF (<error_expression>) THEN <statement> ELSEIF (<valid_expression>) THEN <statement> ELSE <statement> END IF; <continue_statement>;
CASE (<error_expression>) WHEN (<valid_expression>) THEN <statement> ELSE <statement> END CASE; <continue_statement>
CASE (<valid_expression>) WHEN (<error_expression>) THEN <statement> WHEN (<valid_expression>) THEN <statement> ELSE <statement> END CASE; <continue_statement>
FOR i IN <valid_expression> TO <error_expression> DO <statement> END FOR <continue_statement>
WHILE <error_expression> DO <statement> END WHILE; <continue_statement>
REPEAT <statement> UNTIL <error_expression>; <continue_statement>
RETURN <error_expression>; <continue_statement>
DECLARE x int := 0; myproc PROCEDURE() RETURNS STRING AS BEGIN x := <error_expression>; <statement> END; BEGIN CALL myproc(); <continue_statement> ... END;
示例¶
以下示例展示了如何声明和触发异常,以及如何使用异常处理程序处理这些异常:
处理多种类型的异常¶
以下示例展示了一个设计用于处理多种类型异常的异常处理程序:
DECLARE
result VARCHAR;
exception_1 EXCEPTION (-20001, 'I caught the expected exception.');
exception_2 EXCEPTION (-20002, 'Not the expected exception!');
BEGIN
result := 'If you see this, I did not catch any exception.';
IF (TRUE) THEN
RAISE exception_1;
END IF;
RETURN result;
EXCEPTION
WHEN exception_2 THEN
RETURN SQLERRM;
WHEN exception_1 THEN
RETURN SQLERRM;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
EXECUTE IMMEDIATE $$
DECLARE
result VARCHAR;
exception_1 EXCEPTION (-20001, 'I caught the expected exception.');
exception_2 EXCEPTION (-20002, 'Not the expected exception!');
BEGIN
result := 'If you see this, I did not catch any exception.';
IF (TRUE) THEN
RAISE exception_1;
END IF;
RETURN result;
EXCEPTION
WHEN exception_2 THEN
RETURN SQLERRM;
WHEN exception_1 THEN
RETURN SQLERRM;
END;
$$;
该输出表明异常处理程序已捕获异常:
+----------------------------------+
| anonymous block |
|----------------------------------|
| I caught the expected exception. |
+----------------------------------+
处理异常并继续¶
以下示例展示了一个带有 CONTINUE 类型 WHEN 子句的异常处理程序:
DECLARE
exception_1 EXCEPTION (-20001, 'Catch and continue');
BEGIN
LET counter := 0;
IF (TRUE) THEN
RAISE exception_1;
END IF;
counter := counter + 10;
RETURN 'Counter value: ' || counter;
EXCEPTION
WHEN exception_1 CONTINUE THEN
counter := counter +1;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
EXECUTE IMMEDIATE $$
DECLARE
exception_1 EXCEPTION (-20001, 'Catch and continue');
BEGIN
LET counter := 0;
IF (TRUE) THEN
RAISE exception_1;
END IF;
counter := counter + 10;
RETURN 'Counter value: ' || counter;
EXCEPTION
WHEN exception_1 CONTINUE THEN
counter := counter +1;
END;
$$;
该输出显示异常处理程序捕获了异常、执行了将计数器增加 1 的语句,然后在捕获异常后执行了下一条语句,将计数器增加了 10:
+-------------------+
| anonymous block |
|-------------------|
| Counter value: 11 |
+-------------------+
以下示例展示了当循环中引发错误时,带有 CONTINUE 类型的 WHEN 子句的异常处理程序如何工作。该示例在第一次迭代时引发错误,因为它尝试将值 10 除以 0。CONTINUE 处理程序将该错误记录在 error_log_table 中,然后块继续进行循环的下一次迭代,将 10 除以 1。循环会继续迭代,直到 10 除以 5 且循环结束。输出为 2:
CREATE TABLE error_log_table (handler_type VARCHAR, error_message VARCHAR);
DECLARE
x INT := 0;
BEGIN
FOR i IN 0 TO 5 DO
x := 10/i;
END FOR;
RETURN x;
EXCEPTION
WHEN EXPRESSION_ERROR CONTINUE THEN
INSERT INTO error_log_table SELECT 'continue_type', :SQLERRM;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
CREATE TABLE error_log_table (handler_type VARCHAR, error_message VARCHAR);
EXECUTE IMMEDIATE $$
DECLARE
x INT := 0;
BEGIN
FOR i IN 0 TO 5 DO
x := 10/i;
END FOR;
RETURN x;
EXCEPTION
WHEN EXPRESSION_ERROR CONTINUE THEN
INSERT INTO error_log_table SELECT 'continue_type', :SQLERRM;
END;
$$;
+-----------------+
| anonymous block |
|-----------------|
| 2 |
+-----------------+
在嵌套块中处理异常¶
以下示例展示了嵌套块,并说明内部块可以引发在内部块或外部块中声明的异常:
DECLARE
e1 EXCEPTION (-20001, 'Exception e1');
BEGIN
-- Inner block.
DECLARE
e2 EXCEPTION (-20002, 'Exception e2');
selector BOOLEAN DEFAULT TRUE;
BEGIN
IF (selector) THEN
RAISE e1;
ELSE
RAISE e2;
END IF;
END;
EXCEPTION
WHEN e1 THEN
RETURN SQLERRM || ' caught in outer block.';
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
EXECUTE IMMEDIATE $$
DECLARE
e1 EXCEPTION (-20001, 'Exception e1');
BEGIN
-- Inner block.
DECLARE
e2 EXCEPTION (-20002, 'Exception e2');
selector BOOLEAN DEFAULT TRUE;
BEGIN
IF (selector) THEN
RAISE e1;
ELSE
RAISE e2;
END IF;
END;
EXCEPTION
WHEN e1 THEN
RETURN SQLERRM || ' caught in outer block.';
END;
$$;
该输出表明异常处理程序已捕获异常:
+-------------------------------------+
| anonymous block |
|-------------------------------------|
| Exception e1 caught in outer block. |
+-------------------------------------+
以下示例与上一个示例类似,但演示了嵌套块,每个块都有自己的异常处理程序。
DECLARE
result VARCHAR;
e1 EXCEPTION (-20001, 'Outer exception e1');
BEGIN
result := 'No error so far (but there will be).';
DECLARE
e1 EXCEPTION (-20101, 'Inner exception e1');
BEGIN
RAISE e1;
EXCEPTION
WHEN e1 THEN
result := 'Inner exception raised.';
RETURN result;
END;
RETURN result;
EXCEPTION
WHEN e1 THEN
result := 'Outer exception raised.';
RETURN result;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
EXECUTE IMMEDIATE $$
DECLARE
result VARCHAR;
e1 EXCEPTION (-20001, 'Outer exception e1');
BEGIN
result := 'No error so far (but there will be).';
DECLARE
e1 EXCEPTION (-20101, 'Inner exception e1');
BEGIN
RAISE e1;
EXCEPTION
WHEN e1 THEN
result := 'Inner exception raised.';
RETURN result;
END;
RETURN result;
EXCEPTION
WHEN e1 THEN
result := 'Outer exception raised.';
RETURN result;
END;
$$;
备注
此示例在外部块和内部块中使用了相同的异常名称 (e1),不建议这样做。
该示例执行此操作是为了说明异常名称的 范围。名为 e1 的两个异常是不同的异常。
外部块中的 e1 处理程序不会处理在内部块中声明并引发的异常 e1。
该输出表明内部异常处理程序已运行:
+-------------------------+
| anonymous block |
|-------------------------|
| Inner exception raised. |
+-------------------------+
处理同一子句中的多种异常及未指定异常¶
以下示例片段展示了如何执行两项任务:
使用
OR捕获同一子句中的多个异常。使用
WHEN OTHER THEN捕获未指定的异常。
EXCEPTION
WHEN MY_FIRST_EXCEPTION OR MY_SECOND_EXCEPTION OR MY_THIRD_EXCEPTION THEN
RETURN 123;
WHEN MY_FOURTH_EXCEPTION THEN
RETURN 4;
WHEN OTHER THEN
RETURN 99;
使用内置变量处理异常¶
以下示例展示了在捕获异常时如何返回 SQLCODE、SQLERRM(SQL 错误消息)及 SQLSTATE 内置变量值:
DECLARE
MY_EXCEPTION EXCEPTION (-20001, 'Sample message');
BEGIN
RAISE MY_EXCEPTION;
EXCEPTION
WHEN STATEMENT_ERROR THEN
RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
WHEN EXPRESSION_ERROR THEN
RETURN OBJECT_CONSTRUCT('Error type', 'EXPRESSION_ERROR',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
WHEN OTHER THEN
RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream or execute_string method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, and Python Connector):
EXECUTE IMMEDIATE $$
DECLARE
MY_EXCEPTION EXCEPTION (-20001, 'Sample message');
BEGIN
RAISE MY_EXCEPTION;
EXCEPTION
WHEN STATEMENT_ERROR THEN
RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
WHEN EXPRESSION_ERROR THEN
RETURN OBJECT_CONSTRUCT('Error type', 'EXPRESSION_ERROR',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
WHEN OTHER THEN
RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
END;
$$;
运行此示例将生成以下输出:
+--------------------------------+
| anonymous block |
|--------------------------------|
| { |
| "Error type": "Other error", |
| "SQLCODE": -20001, |
| "SQLERRM": "Sample message", |
| "SQLSTATE": "P0001" |
| } |
+--------------------------------+