选择是编写存储过程还是用户定义函数

本主题介绍存储过程和 UDFs 之间的主要区别,包括调用方式和功能方面的区别。

概括地说,存储过程和 UDFs 的不同之处在于它们通常的使用方式,如下所述。

存储过程用途

用户定义函数用途

一般通过执行 SQL 语句来执行管理操作。允许(但不是必需)存储过程的正文显式返回值(如错误指示符)。

计算并返回一个值。函数始终通过指定表达式显式返回值。例如,JavaScript UDF 正文必须具有返回值的 return 语句。

本主题内容:

何时创建存储过程或 UDF

通常,在决定是创建存储过程还是 UDF 时,请考虑以下建议:

在以下情况下创建存储过程...

在以下情况下创建 UDF...

  • 要从另一个应用程序/系统迁移现有存储过程。

  • 需要执行 DDL 或 DML 数据库操作:

    • 管理任务,包括 DDL,如删除临时表、删除超过 N 天的数据或添加用户。

    • DML 语句(例如 UPDATE 语句)

  • 要从其他应用程序/系统迁移现有 UDF。

  • 需要一个可以作为 SQL 语句的一部分调用的函数,并且该函数必须返回将在语句中使用的值。

  • 输出需要包含每个输入行或每个组的值。例如:

    SELECT MyFunction(col1) FROM table1;
    
    Copy
  • 需要使用 SQL(例如 SELECT 语句)执行简单的查询。

支持的处理程序语言

编写过程或 UDF 时,使用一种受支持的语言将其逻辑编写为处理程序。下表列出了支持的语言。

存储过程

用户定义的函数

Java

Java

JavaScript

JavaScript

Python

Python

Scala

Scala

Snowflake Scripting

SQL

使用和行为差异

以下各节介绍过程和 UDFs 支持的行为的具体差异。

UDFs 返回值;存储过程不需要返回值

  • UDF 始终通过指定表达式显式返回值。UDF 的用途是计算并返回值。例如,JavaScript UDF 正文必须具有返回值的 return 语句。

  • 允许(但不是必需)存储过程显式返回值(如错误指示符)。存储过程的用途通常是通过执行 SQL 语句来执行管理操作。如果过程未显式返回值,则会隐式返回 NULL。

    请注意,每个 CREATE PROCEDURE 语句都必须包含一个指定返回类型的 RETURNS 子句,即使该过程没有显式返回任何内容。如果过程未显式返回值,则会隐式返回 NULL。

    以下示例中的代码用 RETURNS 子句为过程声明了一个返回类型,但只有在出错时才会返回值。换言之,并非每个代码路径都返回值。

    CREATE OR REPLACE PROCEDURE do_stuff(input NUMBER)
    RETURNS VARCHAR
    LANGUAGE SQL
    AS
    $$
    DECLARE
      ERROR VARCHAR DEFAULT 'Bad input. Number must be less than 10.';
    
    BEGIN
      IF (input > 10) THEN
        RETURN ERROR;
      END IF;
    
      -- Perform an operation that doesn't return a value.
    
    END;
    $$
    ;
    
    Copy

UDF 返回值可直接用于 SQL;存储过程返回值不能

如果不是从 Snowflake Scripting 块中调用存储过程,则不能在 SQL 中直接使用存储过程返回的值(与函数返回的值不同)。CALL 命令的语法不提供存储返回值的位置,也不提供对其进行操作或将值传递给其他操作的方法。换言之,以下语句不是有效 SQL 语句:

y = stored_procedure1(x);                         -- Not allowed.
Copy

如果在 Snowflake Scripting 块 获取存储过程 返回的值。

还可以间接使用存储过程的返回值(在 Snowflake Scripting 块之外),如以下列表所述:

  • 可以在另一个存储过程中调用该存储过程。例如,当用 JavaScript 编写存储过程处理程序时,外部存储过程中的 JavaScript 可以检索和存储内部存储过程的输出。但是,请记住,外部存储过程(以及每个内部存储过程)仍然无法向其调用方返回多个值。

  • 可以调用存储过程,然后调用 RESULT_SCAN 函数并向其传递为存储过程生成的语句 ID。

  • 可以将结果集存储在临时表或永久表中,并在从存储过程调用返回后使用该表。

  • 如果数据量不太大,可以在一个 VARIANT 中存储多行和多列(例如,作为 JSON 值)并返回该 VARIANT。

UDFs 可以在另一个语句的上下文中调用;存储过程是独立调用的

  • UDF 的计算结果为一个值,可在可以使用通用表达式的上下文中使用,如下所示:

    SELECT MyFunction_1(column_1) FROM table1;
    
    Copy
  • 存储过程的计算结果不为值,并且不能在可以使用常规表达式的所有上下文中使用。例如,不能执行 SELECT my_stored_procedure()...

    将存储过程作为独立语句调用,如以下示例所示:

    CALL MyStoredProcedure_1(argument_1);
    
    Copy

有关调用函数和过程的更多详细信息,请参阅以下内容:

可以用一个语句调用多个 UDFs;用一个语句调用单个存储过程

  • 单个 SQL 语句可以调用多个 UDFs。

  • 单个 SQL 语句只能调用一个存储过程。

    同样,存储过程与 UDF 不同,不能作为表达式的一部分进行调用。但是,在存储过程中,存储过程可以调用另一个存储过程,也可以以递归方式调用自身。例如,请参阅代码示例部分 示例

有关调用函数和过程的更多详细信息,请参阅以下内容:

UDFs 只能通过简单的查询来访问数据库;存储过程可以执行 DDL 和 DML 语句

  • 在 UDF 中,只能使用 SQL 执行查询(不能使用 DML 或 DDL 语句)。

  • 在存储过程中,您可以执行数据库操作,如 SELECT、UPDATE 和 CREATE:

    • 例如,在 JavaScript 存储过程中,可以使用 JavaScript API 来执行这些操作。

      下面的示例演示了存储过程如何创建和执行 SQL 语句,以调用另一个存储过程。$$ 表示存储过程中 JavaScript 处理程序代码的开始和结束。

      CREATE PROCEDURE ...
        $$
        // Create a Statement object that can call a stored procedure named
        // MY_PROCEDURE().
        var stmt1 = snowflake.createStatement( { sqlText: "call MY_PROCEDURE(22)" } );
        // Execute the SQL command; in other words, call MY_PROCEDURE(22).
        stmt1.execute();
        // Create a Statement object that executes a SQL command that includes
        // a call to a UDF.
        var stmt2 = snowflake.createStatement( { sqlText: "select MY_UDF(column1) from table1" } );
        // Execute the SQL statement and store the output (the "result set") in
        // a variable named "rs", which we can access later.
        var rs = stmt2.execute();
        // etc.
        $$;
      
      Copy
    • Snowflake Scripting 存储过程中,可以执行 SQL 语句。

      下面的示例演示了存储过程如何创建和执行 SQL 语句,以调用另一个存储过程。$$ 表示存储过程中 Snowflake Scripting 代码的开始和结束。

      CREATE PROCEDURE ...
        -- Call a stored procedure named my_procedure().
        CALL my_procedure(22);
        -- Execute a SQL statement that includes a call to a UDF.
        SELECT my_udf(column1) FROM table1;
      
      Copy
语言: 中文