EXCEPTION (Snowflake Scripting)

指定如何处理 Snowflake Scripting 块中引发的异常。

有关异常的详细信息,请参阅 处理异常

另请参阅:

RAISE

语法

EXCEPTION
    WHEN <exception_name> [ OR <exception_name> ... ] [ { EXIT | CONTINUE } ] THEN
        <statement>;
        [ <statement>; ... ]
    [ WHEN ... ]
    [ WHEN OTHER [ { EXIT | CONTINUE } ] THEN ]
        <statement>;
        [ <statement>; ... ]
Copy

其中:

exception_name

当前块的 DECLARE 部分 或封闭块中定义的异常名称。

statement

语句可以是以下任一语句:

  • 单个 SQL 语句(包括 CALL)。

  • 控制流语句(例如 循环 语句或 分支 语句)。

  • 嵌套 区块

使用说明

  • 每个 都可以有自己的异常处理程序。

  • Snowflake 支持每个块不超过一个异常处理程序。但是,通过使用多个 WHEN 子句,该处理程序可以捕获多种类型的异常。

  • WHEN OTHER [ { EXIT | CONTINUE } ] THEN 子句捕获任何尚未指定的异常。

  • 异常处理程序适用于声明它的块中 BEGIN 与 EXCEPTION 部分之间的语句。它不适用于该块的 DECLARE 部分。

  • 仅当指定的异常在 范围 内时,异常处理程序才能处理该指定的异常。

  • 如果存储过程旨在返回值,则它应从每个可能的退出路径(包括异常处理程序中 EXIT 类型的 每个 WHEN 子句)返回一个值。

  • 若要在异常处理程序中使用变量,该变量必须在 DECLARE 部分或作为实参传递给存储过程。该变量不能在 BEGIN ...END 部分声明。有关更多信息,请参阅 在 Snowflake Scripting 中将变量传递给异常处理程序

  • 发生异常时,系统将按顺序检查处理程序条件并使用首个匹配的 WHEN 子句。在同一块内的顺序是从上到下,且会先检查内部块,然后再检查外部块。系统不会优先选择 EXITCONTINUE 处理程序,哪个先匹配就执行哪个。

  • 每条语句仅能匹配一个处理程序。但是,在异常处理程序主体内部遇到的所有异常都可以触发外部块异常处理程序。

  • 在异常处理程序中,每个 WHEN 子句可以是以下类型之一:

    • EXIT – 块会执行处理程序中的语句,然后退出当前块。如果块运行此类型的异常,并且块在异常处理程序之后包含语句,则系统不会运行这些语句。

      如果块是内部块,并且异常处理程序不包含 RETURN 语句,则执行将退出内部块并继续执行外部块中的代码。

      EXIT 是默认值。

    • CONTINUE - 块执行处理程序中的语句,并继续执行紧随导致错误的语句之后的语句。

    一个 EXCEPTION 子句可以同时包含 EXITCONTINUE 类型的 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;
    
    Copy
    LET x := <valid_expression>;
    x := <error_expression>;
    <continue_statement>
    
    Copy
    SELECT <statement> INTO <error_expression>;
    <continue_statement>;
    
    Copy
    IF (<error_expression>) THEN
      <statement>
    ELSEIF (<valid_expression>) THEN
      <statement>
    ELSE
      <statement>
    END IF;
    <continue_statement>;
    
    Copy
    CASE (<error_expression>)
      WHEN (<valid_expression>) THEN
        <statement>
      ELSE
        <statement>
    END CASE;
    <continue_statement>
    
    Copy
    CASE (<valid_expression>)
      WHEN (<error_expression>) THEN
        <statement>
      WHEN (<valid_expression>) THEN
        <statement>
      ELSE
        <statement>
    END CASE;
    <continue_statement>
    
    Copy
    FOR i IN <valid_expression> TO <error_expression> DO
      <statement>
    END FOR
    <continue_statement>
    
    Copy
    WHILE <error_expression> DO
      <statement>
    END WHILE;
    <continue_statement>
    
    Copy
    REPEAT
      <statement>
    UNTIL <error_expression>;
    <continue_statement>
    
    Copy
    RETURN <error_expression>;
    <continue_statement>
    
    Copy
    DECLARE
      x int := 0;
      myproc PROCEDURE()
        RETURNS STRING
        AS BEGIN
          x := <error_expression>;
          <statement>
        END;
    BEGIN
      CALL myproc();
      <continue_statement>
      ...
    END;
    
    Copy

示例

以下示例展示了如何声明和触发异常,以及如何使用异常处理程序处理这些异常:

处理多种类型的异常

以下示例展示了一个设计用于处理多种类型异常的异常处理程序:

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

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;
$$;
Copy

该输出表明异常处理程序已捕获异常:

+----------------------------------+
| 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;
Copy

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;
$$;
Copy

该输出显示异常处理程序捕获了异常、执行了将计数器增加 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;
Copy

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;
$$;
Copy
+-----------------+
| 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;
Copy

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;
$$;
Copy

该输出表明异常处理程序已捕获异常:

+-------------------------------------+
| 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;
Copy

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;
$$;
Copy

备注

此示例在外部块和内部块中使用了相同的异常名称 (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;
Copy

使用内置变量处理异常

以下示例展示了在捕获异常时如何返回 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;
Copy

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;
$$;
Copy

运行此示例将生成以下输出:

+--------------------------------+
| anonymous block                |
|--------------------------------|
| {                              |
|   "Error type": "Other error", |
|   "SQLCODE": -20001,           |
|   "SQLERRM": "Sample message", |
|   "SQLSTATE": "P0001"          |
| }                              |
+--------------------------------+