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 子句,该处理程序可以捕获多种类型的异常。

  • The WHEN OTHER [ { EXIT | CONTINUE } ] THEN clause catches any exception not yet specified.

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

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

  • If a stored procedure is intended to return a value, then it should return a value from each possible exit path, including each WHEN clause of EXIT type in the exception handler.

  • 若要在异常处理程序中使用变量,该变量必须在 DECLARE 部分或作为实参传递给存储过程。该变量不能在 BEGIN ...END 部分声明。有关更多信息,请参阅 Passing variables to an exception handler in 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

示例

The following examples declare and raise an exceptions, and handle the exceptions with exception handlers:

处理多种类型的异常

The following example shows an exception handler that is designed to handle more than one type of exception:

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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

The output shows that the exception handler caught the exception:

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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

在嵌套块中处理异常

This following example demonstrates nested blocks, and shows that an inner block can raise an exception declared in either the inner block or in an outer block:

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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

The output shows that the exception handler caught the exception:

+-------------------------------------+
| anonymous block                     |
|-------------------------------------|
| Exception e1 caught in outer block. |
+-------------------------------------+

This following example is similar to the previous example, but demonstrates nested blocks, each of which has its own exception handler:

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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

备注

This example uses the same exception name (e1) in the outer and inner blocks, which isn't recommended.

该示例执行此操作是为了说明异常名称的 范围。名为 e1 的两个异常是不同的异常。

The e1 handler in the outer block doesn't handle the exception e1 that is declared and raised in the inner block.

The output shows that the inner exception handler ran:

+-------------------------+
| anonymous block         |
|-------------------------|
| Inner exception raised. |
+-------------------------+

处理同一子句中的多种异常及未指定异常

The following example fragment shows how to perform two tasks:

  • Catch more than one exception in the same clause by using OR.

  • Catch unspecified exceptions by using 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

使用内置变量处理异常

The following example shows how to return SQLCODE, SQLERRM (SQL error message), and SQLSTATE built-in variable values when catching an exception:

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

注意:如果您在 Python Connector 代码中使用 Snowflake CLISnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 Snowflake CLI、SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

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"          |
| }                              |
+--------------------------------+
语言: 中文