CALL(使用匿名过程)

创建和调用一个匿名过程,该过程类似于 存储过程,但不会存储以备将来使用。

使用此命令,您可以创建由 WITH 子句中的参数定义的匿名过程,并调用该过程。

使用此命令时,您不需要具有 CREATE PROCEDURE 架构权限的角色。

该过程使用 调用方权限 运行,这意味着该过程在运行的时候使用调用方权限、当前会话上下文,并且可访问调用方的会话变量和参数。

另请参阅:

CREATE PROCEDURECALL

语法

Java 和 Scala

WITH <name> AS PROCEDURE ([ <arg_name> <arg_data_type> ]) [ , ... ] )
  RETURNS { <result_data_type> [ [ NOT ] NULL ] | TABLE ( [ <col_name> <col_data_type> [ , ... ] ] ) }
  LANGUAGE { SCALA | JAVA }
  RUNTIME_VERSION = '<scala_or_java_runtime_version>'
  PACKAGES = ( 'com.snowflake:snowpark:<version>' [, '<package_name_and_version>' ...] )
  [ IMPORTS = ( '<stage_path_and_file_name_to_read>' [, '<stage_path_and_file_name_to_read>' ...] ) ]
  HANDLER = '<fully_qualified_method_name>'
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ AS '<procedure_definition>' ]
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

对于带有 暂存处理程序 的 Java 和 Scala 过程,请使用以下语法:

WITH <name> AS PROCEDURE ([ <arg_name> <arg_data_type> ]) [ , ... ] )
  RETURNS { <result_data_type> [ [ NOT ] NULL ] | TABLE ( [ <col_name> <col_data_type> [ , ... ] ] ) }
  LANGUAGE { SCALA | JAVA }
  RUNTIME_VERSION = '<scala_or_java_runtime_version>'
  PACKAGES = ( 'com.snowflake:snowpark:<version>' [, '<package_name_and_version>' ...] )
  [ IMPORTS = ( '<stage_path_and_file_name_to_read>' [, '<stage_path_and_file_name_to_read>' ...] ) ]
  HANDLER = '<fully_qualified_method_name>'
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

JavaScript

WITH <name> AS PROCEDURE ([ <arg_name> <arg_data_type> ]) [ , ... ] )
  RETURNS <result_data_type> [ [ NOT ] NULL ]
  LANGUAGE JAVASCRIPT
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  AS '<procedure_definition>'
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

Python

对于内联过程,请使用以下语法:

WITH <name> AS PROCEDURE ( [ <arg_name> <arg_data_type> ] [ , ... ] )
  RETURNS { <result_data_type> [ [ NOT ] NULL ] | TABLE ( [ <col_name> <col_data_type> [ , ... ] ] ) }
  LANGUAGE PYTHON
  RUNTIME_VERSION = '<python_version>'
  PACKAGES = ( 'snowflake-snowpark-python[==<version>]'[, '<package_name>[==<version>]' ... ])
  [ IMPORTS = ( '<stage_path_and_file_name_to_read>' [, '<stage_path_and_file_name_to_read>' ...] ) ]
  HANDLER = '<function_name>'
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
  AS '<procedure_definition>'
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

对于代码位于暂存区文件中的过程,请使用以下语法:

WITH <name> AS PROCEDURE ( [ <arg_name> <arg_data_type> ] [ , ... ] )
  RETURNS { <result_data_type> [ [ NOT ] NULL ] | TABLE ( [ <col_name> <col_data_type> [ , ... ] ] ) }
  LANGUAGE PYTHON
  RUNTIME_VERSION = '<python_version>'
  PACKAGES = ( 'snowflake-snowpark-python[==<version>]'[, '<package_name>[==<version>]' ... ])
  [ IMPORTS = ( '<stage_path_and_file_name_to_read>' [, '<stage_path_and_file_name_to_read>' ...] ) ]
  HANDLER = '<module_file_name>.<function_name>'
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

Snowflake Scripting

WITH <name> AS PROCEDURE ([ <arg_name> <arg_data_type> ]) [ , ... ] )
  RETURNS { <result_data_type> | TABLE ( [ <col_name> <col_data_type> [ , ... ] ] ) }
  LANGUAGE SQL
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  AS '<procedure_definition>'
  [ , <cte_nameN> [ ( <cte_column_list> ) ] AS ( SELECT ...  ) ]
CALL <name> ( [ [ <arg_name> => ] <arg> , ... ] )
  [ INTO :<snowflake_scripting_variable> ]
Copy

必填参数

所有语言

WITH name AS PROCEDURE ( [ arg_name arg_data_type ] [ , ... ] )

指定该过程的标识符 (name) 和任何输入实参。

  • 对于标识符:

    • 标识符必须以字母字符开头,且不能包含空格或特殊字符,除非整个标识符字符串放在双引号内(例如 "My object")。放在双引号内的标识符也区分大小写。请参阅 标识符要求

  • 对于输入实参:

RETURNS result_data_type [ [ NOT ] NULL ]

指定过程返回的结果的类型。

使用 NOT NULL 指定过程必须仅返回非 null 值;默认为 NULL,表示过程可以返回 NULL。

  • 对于 result_data_type,使用与所用语言类型相对应的 Snowflake 数据类型。

    备注

    使用 Java 或 Scala 编写的过程必须有返回值。在 Python 中,如果一个过程不返回任何值,则会被视为返回 None

    请注意,无论使用哪种处理程序语言,此命令的 WITH 子句都必须包含定义返回类型的 RETURNS 子句,即使该过程没有显式返回任何内容也是如此。

  • 对于 RETURNS TABLE ( [ col_name col_data_type [ , ... ] ] ),如果知道返回表中各列的 Snowflake 数据类型,请指定列名称和类型:

    WITH get_top_sales() AS PROCEDURE
      RETURNS TABLE (sales_date DATE, quantity NUMBER)
      ...
    CALL get_top_sales();
    
    Copy

    否则(例如,如果您在运行时确定列类型),则可以省略列名称和类型:

    WITH get_top_sales() AS PROCEDURE
      ...
      RETURNS TABLE ()
    CALL get_top_sales();
    
    Copy

    备注

    目前,在 RETURNS TABLE(...) 子句中,您无法指定 GEOGRAPHY 为列类型。这在创建存储过程和匿名过程中均适用。

    CREATE OR REPLACE PROCEDURE test_return_geography_table_1()
      RETURNS TABLE(g GEOGRAPHY)
      ...
    
    Copy
    WITH test_return_geography_table_1() AS PROCEDURE
      RETURNS TABLE(g GEOGRAPHY)
      ...
    CALL test_return_geography_table_1();
    
    Copy

    如果尝试指定 GEOGRAPHY 为列类型,则调用存储过程会导致该错误:

    Stored procedure execution error: data type of returned table does not match expected returned table type
    
    Copy

    要解决这个问题,可以省略 RETURNS TABLE() 中的列实参和类型。

    CREATE OR REPLACE PROCEDURE test_return_geography_table_1()
      RETURNS TABLE()
      ...
    
    Copy
    WITH test_return_geography_table_1() AS PROCEDURE
      RETURNS TABLE()
      ...
    CALL test_return_geography_table_1();
    
    Copy

    仅当处理程序以下列语言编写时,才支持 RETURNS TABLE(...)

实际上,在 Snowflake Scripting 块 之外,无法使用返回值,因为该调用不能成为表达式的一部分

LANGUAGE language

指定过程处理程序代码所用的语言。

目前,language 支持的值包括:

  • JAVA`(适用于 :doc:`Java </developer-guide/stored-procedure/stored-procedures-java>

  • JAVASCRIPT`(适用于 :doc:`JavaScript </developer-guide/stored-procedure/stored-procedures-javascript>

  • PYTHON`(适用于 :doc:`Python </developer-guide/stored-procedure/stored-procedures-python>

  • SCALA`(适用于 :doc:`Scala </developer-guide/stored-procedure/stored-procedures-scala>

  • SQL`(适用于 :doc:`Snowflake Scripting </developer-guide/snowflake-scripting/index>

AS procedure_definition

定义该过程执行的代码。该定义可以包含任何有效的代码。

请注意以下事项:

  • 对于代码未内联的过程,省略 AS 子句。这包括 处理程序位于暂存区上 的过程。

    相反,使用 IMPORTS 子句指定包含过程代码的文件的位置。有关详细信息,请参阅:

  • 即使在 Snowflake Scripting 中,也必须在 procedure definition 两边使用 字符串字面量分隔符'$$)。

  • 对于 JavaScript 中的过程,如果要编写包含换行符的字符串,则可以在字符串两边使用反引号。

    以下 JavaScript 过程示例使用 $$ 和反引号,因为过程的正文包含单引号和双引号:

    WITH proc3 AS PROCEDURE ()
      RETURNS VARCHAR
      LANGUAGE javascript
      AS
      $$
      var rs = snowflake.execute( { sqlText:
          `INSERT INTO table1 ("column 1")
              SELECT 'value 1' AS "column 1" ;`
          } );
      return 'Done.';
      $$
    CALL proc3();
    
    Copy
  • Snowflake 不会验证处理程序代码。但在执行命令时,无效的处理程序代码会引发错误。

有关存储过程的更多详细信息,请参阅 使用存储过程

CALL name ( [ [ arg_name => ] arg , ... ] )

指定要调用的过程的标识符 (name) 和任何输入实参。

您可以按名称 (arg_name => arg) 或位置 (arg) 指定输入实参。

请注意以下事项:

  • 必须按名称或位置指定所有实参。不能按名称指定某些实参,也不能按位置指定其他实参。

    按名称指定实参时,不能在实参名称前后使用双引号。

  • 如果两个函数或两个过程的名称相同但实参类型不同,那么在实参名称不同的情况下,可以使用实参名称来指定要执行的函数或过程。请参阅 重载过程和函数

Java、Python 或 Scala

RUNTIME_VERSION = 'language_runtime_version'

要使用的语言运行时版本。当前,支持的版本是:

  • Java:11

  • Python:

    • 3.8

    • 3.9

    • 3.10

    • 3.11

  • Scala:2.12

PACKAGES = ( 'snowpark_package_name' [, 'package_name' ...] )

在 Snowflake 中所部署包的名称的逗号分隔列表,应包含在处理程序代码执行环境中。Snowpark 包对于过程是必需的,因此必须始终在 PACKAGES 子句中引用该包。有关 Snowpark 的更多信息,请参阅 Snowpark API

默认情况下,Snowflake 运行过程的环境包括一组受支持语言的选定包。当您在 PACKAGES 子句中引用这些包时,无需在 IMPORTS 子句中引用包含该包的文件,因为该包已在 Snowflake 中可用。

有关给定语言支持的包和版本的列表,请查询 INFORMATION_SCHEMA.PACKAGES 视图,并指定语言。例如:

SELECT * FROM information_schema.packages WHERE language = '<language>';
Copy

其中 languagejavapythonscala

PACKAGES 子句中引用包的语法因包所用的语言而异,如下文所述。

  • Java

    使用以下格式指定包名称和版本号:

    domain:package_name:version
    
    Copy

    要指定最新版本,请为 version 指定 latest

    例如,要在 Snowflake 中加入来自新版 Snowpark 库的包,请使用以下方法:

    PACKAGES = ('com.snowflake:snowpark:latest')
    
    Copy

    从 Snowpark 库中指定包时,必须指定版本 1.3.0 或更高版本。

  • Python

    Snowflake 包含大量可通过 Anaconda 购买的软件包;有关更多信息,请参阅 使用第三方包

    使用以下格式指定包名称和版本号:

    package_name[==version]
    
    Copy

    要指定新版本,请省略版本号。

    例如,要包括空间包版本 2.3.5(以及所需的 Snowpark 包的新版本),请使用以下命令:

    PACKAGES = ('snowflake-snowpark-python', 'spacy==2.3.5')
    
    Copy

    从 Snowpark 库中指定包时,必须指定版本 0.4.0 或更高版本。省略版本号即可使用 Snowflake 中可用的最新版本。

  • Scala

    使用以下格式指定包名称和版本号:

    domain:package_name:version
    
    Copy

    要指定最新版本,请为 version 指定 latest

    例如,要在 Snowflake 中加入来自新版 Snowpark 库的包,请使用以下方法:

    PACKAGES = ('com.snowflake:snowpark:latest')
    
    Copy

    Snowflake 支持在 Scala 过程中使用 Snowpark 版本 0.9.0 或更高版本。但是请注意,这些版本有局限性。例如,1.1.0 之前的版本不支持在过程中使用事务。

HANDLER = 'fully_qualified_method_name'
  • Python

    使用过程的函数或方法的名称。这可能会有所不同,具体取决于代码是内联还是在某个暂存区引用。

    • 当代码为内联时,您只需指定函数名称,如以下示例所示:

      WITH myproc AS PROCEDURE()
        ...
        HANDLER = 'run'
        AS
        $$
        def run(session):
          ...
        $$
      CALL myproc();
      
      Copy
    • 从暂存区导入代码时,将完全受限的处理程序函数名称指定为 <module_name>.<function_name>

      WITH myproc AS PROCEDURE()
        ...
        IMPORTS = ('@mystage/my_py_file.py')
        HANDLER = 'my_py_file.run'
      CALL myproc();
      
      Copy
  • Java 和 Scala

    使用过程的方法或函数的完全限定名称。这通常采用以下形式:

    com.my_company.my_package.MyClass.myMethod
    
    Copy

    其中:

    com.my_company.my_package
    
    Copy

    对应于包含该对象或类的包:

    package com.my_company.my_package;
    
    Copy

可选参数

所有语言

CALLED ON NULL INPUT . RETURNS NULL ON NULL INPUT | STRICT

指定使用 null 输入调用过程时的行为。与系统定义的函数(当任何输入为 null 时总是返回 null)相反,过程可以处理 null 输入,即使输入为 null 也返回非 null 值:

  • CALLED ON NULL INPUT 将始终调用具有 null 输入的过程。对此类值的适当处理由程序决定。

  • 如果任何输入为 null,则 RETURNS NULL ON NULL INPUT`(或其同义词 :samp:`STRICT)不会调用过程,因此不会执行过程中的语句。相反,该行将始终返回 null 值。请注意,对于非 null 输入,该过程可能仍返回 null 值。

默认:CALLED ON NULL INPUT

INTO :snowflake_scripting_variable

将指定的 Snowflake Scripting 变量 设置为存储过程的返回值。

Java、Python 或 Scala

IMPORTS = ( 'stage_path_and_file_name_to_read' [, 'stage_path_and_file_name_to_read' ...] )

要导入的文件的位置(暂存区)、路径和名称。必须将 IMPORTS 子句设置为包括过程所依赖的所有文件:

  • 如果要编写内联过程,则可以省略此子句,除非代码依赖于在过程或资源文件外部定义的类。

  • Java 或 Scala:如果要编写的过程的处理程序将是编译后的代码,则还必须包括包含该过程处理程序的 JAR 文件的路径。

  • Python:如果过程的代码将位于暂存区中,则还必须包含代码所在的模块文件的路径。

IMPORTS 子句中的每个文件都必须具有唯一的名称,即使这些文件位于不同的子目录或不同的暂存区。

使用说明

一般用法

  • 过程不是原子的;如果过程中的一条语句失败,过程中的其他语句不一定会回滚。有关过程和事务的信息,请参阅 事务管理

  • 一个过程只能返回单个值,例如字符串(例如,成功/失败指示符)或数字(例如,错误代码)。如果您需要返回更广泛的信息,则可以返回包含由分隔符(例如逗号)分隔的值的 VARCHAR,也可以返回半结构化数据类型,例如 VARIANT

  • 关于元数据:

    注意

    客户应确保在使用 Snowflake 服务时,不会将个人数据(用户对象除外)、敏感数据、出口管制数据或其他受监管数据作为元数据输入。有关更多信息,请参阅 Snowflake 中的元数据字段

语法

  • 与将 WITH 子句与 SELECT 语句一起使用时类似,除了过程定义外,与 CALL 一起使用的 WITH 子句还支持指定以逗号分隔的多个 CTEs。但不能将 WITH 子句生成的表格值传递给 CALL 子句。

    不过可以指定一个在 WITH 子句中赋值的简单变量。

  • 在语法中,CALL 子句必须出现在最后。

权限

  • 使用此命令创建和调用过程不需要具有 CREATE PROCEDURE 架构权限的角色。

  • 过程的处理程序代码只能执行指定给运行此命令的人员的角色所允许的操作。

特定于语言的限制

示例

以下示例创建并调用一个过程,并按位置指定实参:

WITH copy_to_table AS PROCEDURE (fromTable STRING, toTable STRING, count INT)
  RETURNS STRING
  LANGUAGE SCALA
  RUNTIME_VERSION = '2.12'
  PACKAGES = ('com.snowflake:snowpark:latest')
  HANDLER = 'DataCopy.copyBetweenTables'
  AS
  $$
    object DataCopy
    {
      def copyBetweenTables(session: com.snowflake.snowpark.Session, fromTable: String, toTable: String, count: Int): String =
      {
        session.table(fromTable).limit(count).write.saveAsTable(toTable)
        return "Success"
      }
    }
  $$
  CALL copy_to_table('table_a', 'table_b', 5);
Copy

以下示例创建并调用一个过程,并按名称指定实参:

WITH copy_to_table AS PROCEDURE (fromTable STRING, toTable STRING, count INT)
  RETURNS STRING
  LANGUAGE SCALA
  RUNTIME_VERSION = '2.12'
  PACKAGES = ('com.snowflake:snowpark:latest')
  HANDLER = 'DataCopy.copyBetweenTables'
  AS
  $$
    object DataCopy
    {
      def copyBetweenTables(session: com.snowflake.snowpark.Session, fromTable: String, toTable: String, count: Int): String =
      {
        session.table(fromTable).limit(count).write.saveAsTable(toTable)
        return "Success"
      }
      }
    }
  $$
  CALL copy_to_table(
    toTable => 'table_b',
    count => 5,
    fromTable => 'table_a');
Copy

如需查看其他示例,请参阅以下主题:

有关过程示例,请参阅 使用存储过程

语言: 中文