处理异常¶
在 Snowflake Scripting 块中,如果发生错误,可以引发异常。您还可以处理 Snowflake Scripting 代码中发生的异常。
在 Snowflake Scripting 中处理异常简介¶
如果在执行语句时发生错误,Snowflake Scripting 会引发异常。例如,如果语句尝试删除不存在的表,Snowflake Scripting 会引发异常。
在 Snowflake Scripting 块中,您可以编写异常处理程序,以捕获该块中以及嵌套在该块中的块中声明的特定类型的异常。此外,对于代码中可能发生的错误,您可以定义自己的异常,以便在发生错误时引发这些异常。
处理程序中的语句运行后,可以选择退出块或继续运行块中的语句。有关更多信息,请参阅 处理 Snowflake Scripting 中的异常。
当 Snowflake Scripting 块中引发异常时(由代码或无法执行的语句引发),Snowflake Scripting 会尝试查找该异常的处理程序:
如果发生异常的块具有该异常的处理程序,则在该异常处理程序的开头恢复执行。
如果该块没有自己的异常处理程序,则封闭块可以捕获该异常。
如果异常发生的深度超过一层,则系统每次将异常向上发送一层,直到出现以下任一情况:
具有适当异常处理程序的层可以处理该异常。
到达最外层,在这种情况下会发生错误。
如果当前块或任何封闭块中没有异常处理程序,则块的执行将停止,提交块以供执行的客户端(例如 Snowsight、SnowSQL 等)会将此报告为 Snowflake 错误。
异常处理程序可以包含自己的异常处理程序,以防在处理其他异常时发生异常。
在 Snowflake Scripting 中声明异常¶
您可以在块的 DECLARE 部分中声明自己的异常。请使用 异常声明语法 中描述的语法。例如:
DECLARE
my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
在 Snowflake Scripting 中引发已声明的异常¶
要引发异常,请执行 RAISE 命令。例如:
DECLARE
my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN counter;
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 (-20002, 'Raised MY_EXCEPTION.');
BEGIN
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN counter;
END;
$$
;
如果没有处理程序,则执行将在引发异常时停止。在本例中,counter 从不递增和返回。
提交此块以供执行的客户端(例如 Snowsight)将报告错误,并指示未捕获异常:
-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
如果要添加代码来处理引发的任何异常(以及语句无法执行时引发的异常),可以编写异常处理程序。请参阅 处理 Snowflake Scripting 中的异常。
备注
在异常处理程序中,如果需要再次引发相同的异常,请参阅 在 Snowflake Scripting 的异常处理程序中再次引发相同的异常。
处理 Snowflake Scripting 中的异常¶
您可以使用 EXCEPTION 子句捕获异常来显式处理异常,也可以允许块将异常传递给封闭块。
在 EXCEPTION 子句中,请使用 WHEN 子句按名称处理异常。您可以处理您声明的异常以及内置异常。目前,Snowflake 提供以下内置异常:
STATEMENT_ERROR:此异常表示执行语句时出错。例如,如果尝试弃用不存在的表,则会引发此异常。
EXPRESSION_ERROR:此异常表示与表达式相关的错误。例如,如果创建了一个计算结果为 VARCHAR 的表达式,并尝试将该表达式的值赋给 FLOAT,则会引发此错误。
异常块中的每个 WHEN 子句可以是以下类型之一:
EXIT – 块会执行处理程序中的语句,然后退出当前块。如果块运行此类型的异常,并且块包含导致错误的语句之后的语句,则不会运行这些语句。
如果块是内部块,并且异常处理程序不包含 RETURN 语句,则执行将退出内部块并继续执行外部块中的代码。
EXIT 是默认值。
CONTINUE - 该块运行异常块中的语句,并继续执行紧随导致错误的语句之后的语句。
CONTINUE 处理程序可以捕获和处理异常,而无需结束引发异常的语句块。使用默认 EXIT 处理程序,当块中发生错误时,流程将中断并将错误返回给调用方。但是,当错误条件没有严重到足以中断流时,您可以使用 CONTINUE 处理程序。
一个 EXCEPTION 子句可以同时包含 EXIT 和 CONTINUE 类型的 WHEN 子句。
发生异常时,可以通过读取以下三个内置变量,获取有关异常的信息:
SQLCODE:这是一个 5 位的带符号整数。对于用户定义的异常,这是 用于声明异常的语法 中显示的
exception_number。SQLERRM:这是一条错误消息。对于用户定义的异常,这是 用于声明异常的语法 中显示的
exception_message。SQLSTATE:这是一个基于 ANSI SQL 标准 SQLSTATE (link removed) 的 5 字符代码。除 ANSI SQL 标准中的值外,Snowflake 还使用了其他值。
当您使用 CONTINUE 类型的 WHEN 子句时,这些内置变量会反映导致 WHEN 子句中异常的错误。在 WHEN 子句中的语句完成,并且语句在块中继续执行之后,这些变量的值将返回引发异常之前的值。
要处理所有其他非内置或未声明的异常,请使用 WHEN OTHER THEN 子句。WHEN OTHER THEN 子句的类型可以是 EXIT 或 CONTINUE。
例如,假设您拥有以下错误日志表来跟踪异常:
CREATE OR REPLACE TABLE test_error_log(
error_type VARCHAR,
error_code VARCHAR,
error_message VARCHAR,
error_state VARCHAR,
error_timestamp TIMESTAMP);
以下匿名块将有关异常的信息插入表中,并将有关异常的信息返回给用户:
小技巧
该示例在 DECLARE 部分中定义了一个异常,随后对该异常进行了处理。对于处理 STATEMENT_ERROR 异常的示例,请移除此行的注释 (--):
-- SELECT 1/0;
要查看处理其他错误的示例,请移除此行的注释:
-- LET var := 1/0;
DECLARE
my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
-- SELECT 1/0;
-- LET var := 1/0;
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN 'My counter value: ' || counter;
EXCEPTION
WHEN STATEMENT_ERROR THEN
INSERT INTO test_error_log VALUES(
'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);
WHEN my_exception THEN
INSERT INTO test_error_log VALUES(
'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);
WHEN OTHER THEN
INSERT INTO test_error_log VALUES(
'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
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 (-20002, 'Raised MY_EXCEPTION.');
BEGIN
-- SELECT 1/0;
-- LET var := 1/0;
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN 'My counter value: ' || counter;
EXCEPTION
WHEN STATEMENT_ERROR THEN
INSERT INTO test_error_log VALUES(
'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);
WHEN my_exception THEN
INSERT INTO test_error_log VALUES(
'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);
WHEN OTHER THEN
INSERT INTO test_error_log VALUES(
'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);
END;
$$
;
对于返回值,此示例通过调用 OBJECT_CONSTRUCT 构造并返回一个包含异常详细信息的对象,从而处理每种类型的异常。此示例生成以下输出:
+--------------------------------------+
| anonymous block |
|--------------------------------------|
| { |
| "Error type": "MY_EXCEPTION", |
| "SQLCODE": -20002, |
| "SQLERRM": "Raised MY_EXCEPTION.", |
| "SQLSTATE": "P0001" |
| } |
+--------------------------------------+
您可以查询 test_error_log 表以确认已记录错误:
SELECT * FROM test_error_log;
+--------------+------------+----------------------+-------------+-------------------------+
| ERROR_TYPE | ERROR_CODE | ERROR_MESSAGE | ERROR_STATE | ERROR_TIMESTAMP |
|--------------+------------+----------------------+-------------+-------------------------|
| MY_EXCEPTION | -20002 | Raised MY_EXCEPTION. | P0001 | 2025-09-05 12:15:00.068 |
+--------------+------------+----------------------+-------------+-------------------------+
前面的示例使用了默认类型 (EXIT) 的 WHEN 子句。如果其中一个 WHEN 子句捕获异常,它会运行 WHEN 子句中的语句,然后退出。因此,以下代码不会运行:
counter := counter + 1;
RETURN 'My counter value: ' || counter;
如果要处理异常,然后继续运行块中的代码,请指定 CONTINUE 类型的 WHEN 子句。以下示例与前面的示例相同,但它指定 CONTINUE 类型的 WHEN 子句并从每个 WHEN 子句中移除 RETURN 语句:
DECLARE
my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
-- SELECT 1/0;
-- LET var := 1/0;
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN 'My counter value: ' || counter;
EXCEPTION
WHEN STATEMENT_ERROR CONTINUE THEN
INSERT INTO test_error_log VALUES(
'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
WHEN my_exception CONTINUE THEN
INSERT INTO test_error_log VALUES(
'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
WHEN OTHER CONTINUE THEN
INSERT INTO test_error_log VALUES(
'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
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 (-20002, 'Raised MY_EXCEPTION.');
BEGIN
-- SELECT 1/0;
-- LET var := 1/0;
LET counter := 0;
LET should_raise_exception := true;
IF (should_raise_exception) THEN
RAISE my_exception;
END IF;
counter := counter + 1;
RETURN 'My counter value: ' || counter;
EXCEPTION
WHEN STATEMENT_ERROR CONTINUE THEN
INSERT INTO test_error_log VALUES(
'STATEMENT_ERROR', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
WHEN my_exception CONTINUE THEN
INSERT INTO test_error_log VALUES(
'MY_EXCEPTION', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
WHEN OTHER CONTINUE THEN
INSERT INTO test_error_log VALUES(
'OTHER', :sqlcode, :sqlerrm, :sqlstate, CURRENT_TIMESTAMP());
END;
$$
;
+---------------------+
| anonymous block |
|---------------------|
| My counter value: 1 |
+---------------------+
输出显示该示例在引发异常后继续运行以下代码:
counter := counter + 1;
RETURN counter;
有关 CONTINUE 处理程序的更多信息,请参阅 EXCEPTION (Snowflake Scripting)。
在极少数情况下,您可能希望不执行任何操作来显式处理异常。这使您能够在发生异常时继续,而不是终止。有关更多信息,请参阅 NULL 命令。
备注
如果需要再次引发相同的异常,请参阅 在 Snowflake Scripting 的异常处理程序中再次引发相同的异常。
如果没有为异常设置处理程序,则提交块以供执行的客户端(例如 Snowsight)会报告错误(如 在 Snowflake Scripting 中引发已声明的异常 中所述)。
-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
在 Snowflake Scripting 的异常处理程序中再次引发相同的异常¶
在某些情况下,您可能需要引发在异常处理程序中捕获的相同异常。在这些情况下,请执行 RAISE 命令,且不指定任何实参。
例如,假设在异常处理期间,您需要先获取有关异常的一些详细信息,然后再引发相同的异常。获取详细信息后,请执行 RAISE 命令:
BEGIN
SELECT * FROM non_existent_table;
EXCEPTION
WHEN OTHER THEN
LET LINE := SQLCODE || ': ' || SQLERRM;
INSERT INTO myexceptions VALUES (:line);
RAISE; -- Raise the same exception that you are handling.
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 $$
BEGIN
SELECT * FROM non_existent_table;
EXCEPTION
WHEN OTHER THEN
LET LINE := SQLCODE || ': ' || SQLERRM;
INSERT INTO myexceptions VALUES (:line);
RAISE; -- Raise the same exception that you are handling.
END;
$$;
在 Snowflake Scripting 中将变量传递给异常处理程序¶
您可以将变量传递给异常处理程序。异常处理程序可以根据变量的值执行代码,变量值可以在错误消息中返回。
要将变量传递给 EXCEPTION 部分中的处理程序,必须在 DECLARE 部分中声明该变量。如果在块的 BEGIN ...END 部分中声明了变量,则无法在 EXCEPTION 部分中访问该变量。
此外,如果您编写的 Snowflake Scripting 存储过程可以接受实参,则可以在异常处理程序中使用这些实参。
例如,以下匿名块将 counter_val 变量的值传递给异常处理程序:
DECLARE
counter_val INTEGER DEFAULT 0;
my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
WHILE (counter_val < 12) DO
counter_val := counter_val + 1;
IF (counter_val > 10) THEN
RAISE my_exception;
END IF;
END WHILE;
RETURN counter_val;
EXCEPTION
WHEN my_exception THEN
RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
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
counter_val INTEGER DEFAULT 0;
my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
WHILE (counter_val < 12) DO
counter_val := counter_val + 1;
IF (counter_val > 10) THEN
RAISE my_exception;
END IF;
END WHILE;
RETURN counter_val;
EXCEPTION
WHEN my_exception THEN
RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
END;
$$
;
该块返回以下错误消息:
+---------------------------------------------------------+
| anonymous block |
|---------------------------------------------------------|
| Error -20002: Counter value 11 exceeds the limit of 10. |
+---------------------------------------------------------+
以下是传入实参的 Snowflake Scripting 存储过程示例。该示例演示了如何在异常处理程序中使用实参:
CREATE OR REPLACE PROCEDURE exception_test_vars(amount INT)
RETURNS TEXT
LANGUAGE SQL
AS
DECLARE
my_exception_1 EXCEPTION (-20002, 'Value too low');
my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
CREATE OR REPLACE TABLE test_order_insert(units INT);
IF (amount < 1) THEN
RAISE my_exception_1;
ELSEIF (amount > 10) THEN
RAISE my_exception_2;
ELSE
INSERT INTO test_order_insert VALUES (:amount);
END IF;
RETURN 'Order inserted successfully.';
EXCEPTION
WHEN my_exception_1 THEN
RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
WHEN my_exception_2 THEN
RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
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 OR REPLACE PROCEDURE exception_test_vars(amount INT)
RETURNS TEXT
LANGUAGE SQL
AS
$$
DECLARE
my_exception_1 EXCEPTION (-20002, 'Value too low');
my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
CREATE OR REPLACE TABLE test_order_insert(units INT);
IF (amount < 1) THEN
RAISE my_exception_1;
ELSEIF (amount > 10) THEN
RAISE my_exception_2;
ELSE
INSERT INTO test_order_insert VALUES (:amount);
END IF;
RETURN 'Order inserted successfully.';
EXCEPTION
WHEN my_exception_1 THEN
RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
WHEN my_exception_2 THEN
RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
END;
$$
;
以下对存储过程的调用显示了预期的输出:
CALL exception_test_vars(7);
+------------------------------+
| EXCEPTION_TEST_VARS |
|------------------------------|
| Order inserted successfully. |
+------------------------------+
CALL exception_test_vars(-3);
+-----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS |
|-----------------------------------------------------------------------|
| Error -20002: Submitted amount -3 is too low (1 or greater required). |
+-----------------------------------------------------------------------+
CALL exception_test_vars(20);
+----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS |
|----------------------------------------------------------------------|
| Error -20003: Submitted amount 20 is too high (exceeds limit of 10). |
+----------------------------------------------------------------------+