雪花动态 SQL:相当于 DBMS_SQL/sp_executesql
Snowflake dynamic SQL: Equivalent of DBMS_SQL/sp_executesql
我正在搜索 sp_executesql/DBMS_SQL 的等效项,它允许执行动态 SQL(最好使用绑定参数)。
背后的基本原理:在SQL中生成代码为运行并从SQL
执行
我知道 Snowflake 没有包含控制流语法(WHILE/IF-THEN/TRY-CATCH)的程序 SQL 组件,并且可以使用存储过程中的 JavaScript 代码来缓解此类构造。
示例场景:
生成任意SQL:此处table生成
SELECT create_table_sql FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())', '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5))
);
输出:
CREATE OR REPLACE TABLE wide5(id INT
,a1 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a2 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a3 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a4 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a5 INT DEFAULT UNIFORM(1, 10000, RANDOM()));
现在的目标是从 WebUI 执行它。我的第一个想法是将它分配给变量。由于大小限制(小挫折)而失败:
SET sql_text = (SELECT create_table_sql FROM ...);
Assignment to 'SQL_TEXT' not done because value exceeds size limit for variables. Its size is 260; the limit is 256 (internal storage size in bytes).
这里应该是类似 EXECUTE IMMEDIATE/EXEC
或其他 RDBMS 已知的参数化对应物。
....(generated_code)
我创建了我自己的过于简单化的版本:
CREATE OR REPLACE PROCEDURE execute_immediate(sql_param STRING)
RETURNS VARCHAR
LANGUAGE javascript
AS
$$
var rs = snowflake.execute( { sqlText: SQL_PARAM});
return 'Done.';
$$;
并将子查询直接作为参数传递(这里雪花闪耀):
CALL execute_immediate(subquery);
示例:
CALL execute_immediate(
SELECT create_table_sql
FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())', '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5)))
);
正在检查 SELECT 查询:SELECT * FROM wide5
已创建 table。
它正在运行,但我相信它可以做得更好。
我尝试但发现不完全令人满意的其他考虑因素和替代方案:
- 生成SQL脚本、执行查询、复制输出、粘贴并执行(需要手动步骤)
- 在JavaScript存储过程中编写代码生成部分(也许,如果真的没有其他方法可用)
动态 SQL 可能难以编写且容易出错,但使用参数 binding/quoting 标识符和限制用户输入仍然是安全的。
可以使用Snowflake Scripting:
DROP TABLE IF EXISTS wide5;
-- to be run with Snowsight
DECLARE
SQL STRING;
BEGIN
SELECT create_table_sql
INTO :SQL
FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())'
, '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5))
);
EXECUTE IMMEDIATE :SQL;
END;
SELECT * FROM wide5;
-- ID A1 A2 A3 A4 A5
我正在搜索 sp_executesql/DBMS_SQL 的等效项,它允许执行动态 SQL(最好使用绑定参数)。
背后的基本原理:在SQL中生成代码为运行并从SQL
执行我知道 Snowflake 没有包含控制流语法(WHILE/IF-THEN/TRY-CATCH)的程序 SQL 组件,并且可以使用存储过程中的 JavaScript 代码来缓解此类构造。
示例场景:
生成任意SQL:此处table生成
SELECT create_table_sql FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())', '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5))
);
输出:
CREATE OR REPLACE TABLE wide5(id INT
,a1 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a2 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a3 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a4 INT DEFAULT UNIFORM(1, 10000, RANDOM())
,a5 INT DEFAULT UNIFORM(1, 10000, RANDOM()));
现在的目标是从 WebUI 执行它。我的第一个想法是将它分配给变量。由于大小限制(小挫折)而失败:
SET sql_text = (SELECT create_table_sql FROM ...);
Assignment to 'SQL_TEXT' not done because value exceeds size limit for variables. Its size is 260; the limit is 256 (internal storage size in bytes).
这里应该是类似 EXECUTE IMMEDIATE/EXEC
或其他 RDBMS 已知的参数化对应物。
....(generated_code)
我创建了我自己的过于简单化的版本:
CREATE OR REPLACE PROCEDURE execute_immediate(sql_param STRING)
RETURNS VARCHAR
LANGUAGE javascript
AS
$$
var rs = snowflake.execute( { sqlText: SQL_PARAM});
return 'Done.';
$$;
并将子查询直接作为参数传递(这里雪花闪耀):
CALL execute_immediate(subquery);
示例:
CALL execute_immediate(
SELECT create_table_sql
FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())', '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5)))
);
正在检查 SELECT 查询:SELECT * FROM wide5
已创建 table。
它正在运行,但我相信它可以做得更好。
我尝试但发现不完全令人满意的其他考虑因素和替代方案:
- 生成SQL脚本、执行查询、复制输出、粘贴并执行(需要手动步骤)
- 在JavaScript存储过程中编写代码生成部分(也许,如果真的没有其他方法可用)
动态 SQL 可能难以编写且容易出错,但使用参数 binding/quoting 标识符和限制用户输入仍然是安全的。
可以使用Snowflake Scripting:
DROP TABLE IF EXISTS wide5;
-- to be run with Snowsight
DECLARE
SQL STRING;
BEGIN
SELECT create_table_sql
INTO :SQL
FROM (
SELECT LISTAGG(REPLACE(CHAR(13) || ',a<index> INT DEFAULT UNIFORM(1, 10000, RANDOM())'
, '<index>', seq8()+1),'')
WITHIN GROUP(ORDER BY seq8()) AS column_list
,REPLACE(REPLACE(
'CREATE OR REPLACE TABLE <table_name>(id INT <column_list>);'
,'<table_name>', 'wide5')
,'<column_list>', column_list) AS create_table_sql
FROM TABLE(GENERATOR(rowcount => 5))
);
EXECUTE IMMEDIATE :SQL;
END;
SELECT * FROM wide5;
-- ID A1 A2 A3 A4 A5