运行时构造 SQL

Snowflake 支持几种不同的技术,用于在运行时动态构造 SQL 语句字符串。通过使用这些技术,您可以为直到运行时才知道 SQL 语句全文的用例指定更通用而灵活的 SQL 字符串。

存储过程或应用程序可以接受用户输入,然后在 SQL 语句中使用该输入。例如,一个表可能存储有关销售订单的信息。应用程序或存储过程可能会接受订单 ID 作为输入,并运行仅返回该特定订单结果的查询。

开发者可以使用包含占位符的 SQL 语句编写存储过程代码或应用程序代码,然后将变量绑定到代码中的这些占位符。这些占位符称为 绑定变量。开发者还可以编写代码,以便从输入字符串构造 SQL 语句(例如,通过连接包含 SQL 命令、参数和值的字符串)。

以下技术可用于在运行时动态构造 SQL 语句:

备注

当程序根据用户输入构造 SQL 语句时,存在潜在的安全风险,例如 SQL 注入。如果 SQL 语句的输入来自外部来源,请确保它们有效。有关更多信息,请参阅 SQL 注入

使用 TO_QUERY 函数

对于动态构造 SQL 语句的存储过程和应用程序,可以在代码中使用 TO_QUERY 函数。此表函数接受 SQL 字符串作为输入。或者,SQL 字符串可以包含参数,您可以指定要作为绑定变量传递给形参的实参。

下面是一个调用函数的简单示例:

SELECT COUNT(*) FROM TABLE(TO_QUERY('SELECT 1'));
Copy
+----------+
| COUNT(*) |
|----------|
|        1 |
+----------+

下面的示例在存储过程中使用 TO_QUERY 函数:

CREATE OR REPLACE PROCEDURE get_num_results_tq(query VARCHAR)
RETURNS TABLE ()
LANGUAGE SQL
AS
DECLARE
  res RESULTSET DEFAULT (SELECT COUNT(*) FROM TABLE(TO_QUERY(:query)));
BEGIN
  RETURN TABLE(res);
END;
Copy

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

CREATE OR REPLACE PROCEDURE get_num_results_tq(query VARCHAR)
RETURNS TABLE ()
LANGUAGE SQL
AS
$$
DECLARE
  res RESULTSET DEFAULT (SELECT COUNT(*) FROM TABLE(TO_QUERY(:query)));
BEGIN
  RETURN TABLE(res);
END;
$$
;
Copy

调用存储过程:

CALL get_num_results_tq('SELECT 1');
Copy
+----------+
| COUNT(*) |
|----------|
|        1 |
+----------+

在存储过程和应用程序中使用动态 SQL

要构造接受用户输入的 SQL 语句,可以在 Snowflake ScriptingJavascript 存储过程或 Snowflake Scripting 匿名块 中使用动态 SQL。您也可以在使用 Snowflake 驱动程序Snowflake SQL API 的应用程序代码中使用动态 SQL。

此示例使用 Snowflake Scripting 创建存储过程。存储过程接受 SQL 文本作为输入,通过向其中附加文本来构造一个包含 SQL 语句的字符串。然后,使用 EXECUTE IMMEDIATE 命令执行动态 SQL。

CREATE OR REPLACE PROCEDURE get_num_results(query VARCHAR)
RETURNS INTEGER
LANGUAGE SQL
AS
DECLARE
  row_count INTEGER DEFAULT 0;
  stmt VARCHAR DEFAULT 'SELECT COUNT(*) FROM (' || query || ')';
  res RESULTSET DEFAULT (EXECUTE IMMEDIATE :stmt);
  cur CURSOR FOR res;
BEGIN
  OPEN cur;
  FETCH cur INTO row_count;
  RETURN row_count;
END;
Copy

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

CREATE OR REPLACE PROCEDURE get_num_results(query VARCHAR)
RETURNS INTEGER
LANGUAGE SQL
AS
$$
DECLARE
  row_count INTEGER DEFAULT 0;
  stmt VARCHAR DEFAULT 'SELECT COUNT(*) FROM (' || query || ')';
  res RESULTSET DEFAULT (EXECUTE IMMEDIATE :stmt);
  cur CURSOR FOR res;
BEGIN
  OPEN cur;
  FETCH cur INTO row_count;
  RETURN row_count;
END;
$$
;
Copy

下面的示例调用此过程:

CALL get_num_results('SELECT 1');
Copy
+-----------------+
| GET_NUM_RESULTS |
|-----------------|
|               1 |
+-----------------+

动态 SQL 支持绑定变量。下面的 Snowflake Scripting 示例使用 ? 占位符表示的绑定变量在运行时动态构造 SQL 语句。此块从以下 invoices 表中选择数据:

CREATE OR REPLACE TABLE invoices (price NUMBER(12, 2));
INSERT INTO invoices (price) VALUES
  (11.11),
  (22.22);
Copy

执行匿名块:

DECLARE
  rs RESULTSET;
  query VARCHAR DEFAULT 'SELECT * FROM invoices WHERE price > ? AND price < ?';
  minimum_price NUMBER(12,2) DEFAULT 20.00;
  maximum_price NUMBER(12,2) DEFAULT 30.00;
BEGIN
  rs := (EXECUTE IMMEDIATE :query USING (minimum_price, maximum_price));
  RETURN TABLE(rs);
END;
Copy

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

EXECUTE IMMEDIATE $$
DECLARE
  rs RESULTSET;
  query VARCHAR DEFAULT 'SELECT * FROM invoices WHERE price > ? AND price < ?';
  minimum_price NUMBER(12,2) DEFAULT 20.00;
  maximum_price NUMBER(12,2) DEFAULT 30.00;
BEGIN
  rs := (EXECUTE IMMEDIATE :query USING (minimum_price, maximum_price));
  RETURN TABLE(rs);
END;
$$
;
Copy
+-------+
| PRICE |
|-------|
| 22.22 |
+-------+

比较用于动态构建 SQL 的技术

下表介绍了用于动态构建 SQL 的技术的优缺点。

技术

优点

缺点

TO_QUERY 函数

  • 简单语法

  • 内置错误处理

  • 动态构造 SQL 的用例的特定语义

  • 自动确定的结果集

  • 在执行之前无法描述或解释查询

  • 仅在 SELECT 语句的 FROM 子句中有效

  • Snowflake 特定

动态 SQL

  • 比 TO_QUERY 函数更通用而灵活

  • 可以在执行之前描述或解释查询

  • 比 TO_QUERY 函数更复杂

  • 手动错误处理

语言: 中文