Snowflake – execute immediate 返回查询文本而非查询结果

huangapple go评论65阅读模式
英文:

Snowflake - execute immediate returns query text rather than the query result

问题

I understand you only want the translated parts. Here is the translated content:

我有一个表格,其中包含用于分析的SQL查询的列。我正在尝试使用"execute immediate"来返回这些SQL查询的结果。

如果我运行以下代码:

execute immediate 'select the_sql from analysis_queries';

它返回的与我只运行以下代码相同:

select the_sql from analysis_queries;

这是select count(*) as usr_ct from users;

我想要返回的是125(实际的usr_ct)。

我尝试过以下代码:

set stmt = (select the_sql from analysis_queries);
execute immediate $stmt;

这确实返回了usr_ct,但对于更长的查询,它会引发错误:"Assignment to 'STMT' not done because value exceeds size limit for variables. Its size is 1,902; the limit is 256 (internal storage size in bytes)."

我对动态SQL/存储过程不熟悉,所以任何帮助都将非常感谢。谢谢!

英文:

I have a table that has a column that contains SQL queries used in analyses. I am trying to use execute immediate to return the results of those SQL queries.

Ex:

INSERT INTO ANALYSIS_QUERIES(DIR_NAME, FILE_NAME, THE_SQL) VALUES
(
'COUNT_CHECK'
,'USER_COUNT_CHECK.sql'
,'select
    count(*) as usr_ct
from users;'
);

If I run

execute immediate 'select the_sql from analysis_queries'

it returns the same thing as if I had just run:

select the_sql from analysis_queries;

which is select count(*) as usr_ct from users;

What I want to return is 125 (the actual usr_ct).

I've tried

set stmt = (select the_sql from analysis_queries);
execute immediate $stmt;

Which does return the usr_ct, but there are longer queries where it throws an error Assignment to 'STMT' not done because value exceeds size limit for variables. Its size is 1,902; the limit is 256 (internal storage size in bytes).

I'm new to dynamic sql/stored procedures, so any help would be amazing.

Thank you,

答案1

得分: 2

警告!执行用户提供的任意查询是不安全的,因为恶意用户可能提供像 DROP DATABASE/DROP TABLE/ALTER USER/... 这样的 SQL 语句。

查询表格:

CREATE OR REPLACE TABLE ANALYSIS_QUERIES(DIR_NAME TEXT, 
                                         FILE_NAME TEXT, 
                                         THE_SQL TEXT, 
                                         RESULT INT) -- result column
AS 
SELECT 'COUNT_CHECK','USER_COUNT_CHECK.sql', 
       'select count(*) as usr_ct from users;', NULL;

125 行的用户表格:

CREATE OR REPLACE TABLE users(col int) AS 
SELECT seq4()
FROM TABLE(GENERATOR(ROWCOUNT=>125));

使用 Snowflake Scripting 块执行代码:

  • 使用 CURSOR 和 FOR CURSOR LOOP 遍历 analysis_queries 表格
  • 使用 EXECUTE IMMEDIATE 执行查询
  • 使用 RESULT_SCAN(LAST_QUERY_ID()) 拦截输出并赋值给变量

代码示例:

DECLARE
   cur CURSOR FOR SELECT * 
                  FROM ANALYSIS_QUERIES
                  --WHERE ...  -- 适用的话添加一些条件
                  ;
   sql_text TEXT;
   dir_name TEXT;
   file_name TEXT;
   res INT;
BEGIN
   FOR c IN cur DO
      sql_text := c.THE_SQL;
      dir_name := c.DIR_NAME;
      file_name := c.FILE_NAME;

      -- 执行查询 
      EXECUTE IMMEDIATE sql_text;

      -- 拦截结果(假设是单行单列)并赋值给变量
      res := (SELECT $1 FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));

      -- 将结果保存回表格
      UPDATE ANALYSIS_QUERIES
      SET result = :res
      WHERE DIR_NAME = :dir_name
        AND FILE_NAME = :file_name;
   END FOR;

   RETURN 'Success';
END;

输出:

SELECT * FROM ANALYSIS_QUERIES;

|DIR_NAME     |FILE_NAME            |THE_SQL                                      |RESULT|
|-------------|---------------------|---------------------------------------------|------|
|COUNT_CHECK  |USER_COUNT_CHECK.sql |select count(*) as usr_ct from users;        |125   |

方法 2:

> I've tried
>
>     set stmt = (select the_sql from analysis_queries);
>     execute immediate $stmt;

使用 Snowflake Scripting 块执行代码:

BEGIN 
   LET THE_SQL TEXT := (SELECT THE_SQL 
                        FROM ANALYSIS_QUERIES 
                        WHERE FILE_NAME = 'USER_COUNT_CHECK.sql');

   LET RES RESULTSET := (EXECUTE IMMEDIATE :THE_SQL);

   RETURN TABLE(RES);
END;

输出:

|USR_CT|
|------|
|125   |

相关:在 SnowSQL 和经典控制台中使用 Snowflake 脚本

英文:

Warning!

Running an arbitrary query provided by user is NOT secure, for instance malicious user may provide sql_text like DROP DATABASE/DROP TABLE/ALTER USER/....


Query table:

CREATE OR REPLACE TABLE ANALYSIS_QUERIES(DIR_NAME TEXT, 
                                         FILE_NAME TEXT, 
                                         THE_SQL TEXT, 
                                         RESULT INT) -- result column
AS 
SELECT 'COUNT_CHECK','USER_COUNT_CHECK.sql', 
       'select count(*) as usr_ct from users;', NULL;

-- 125 rows users table
CREATE OR REPLACE TABLE users(col int) AS 
SELECT seq4()
FROM TABLE(GENERATOR(ROWCOUNT=>125));

SELECT * FROM ANALYSIS_QUERIES;
DIR_NAME FILE_NAME THE_SQL RESULT
COUNT_CHECK USER_COUNT_CHECK.sql select count(*) as usr_ct from users; null

Executing code using Snowflake Scripting block:

  • CURSOR and FOR CURSOR LOOP to iterate over analysis_queries table
  • EXECUTE IMMEDIATE to run the query
  • RESULT_SCAN(LAST_QUERY_ID()) - intercepting the output and assigning to variable

Code:

DECLARE
   cur CURSOR FOR SELECT * 
                  FROM ANALYSIS_QUERIES
                  --WHERE ...  -- some condition if needed
                  ;
   sql_text TEXT;
   dir_name TEXT;
   file_name TEXT;
   res INT;
BEGIN
   FOR c IN cur DO
      sql_text := c.THE_SQL;
      dir_name := c.DIR_NAME;
      file_name := c.FILE_NAME;
     
      -- run the query 
      EXECUTE IMMEDIATE sql_text;

      -- intercept result(assumption single row/single column) to variable
      res := (SELECT $1 FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));

      -- save result back into table
      UPDATE ANALYSIS_QUERIES
      SET result = :res
      WHERE DIR_NAME = :dir_name
        AND FILE_NAME = :file_name;
   END FOR;

   RETURN 'Success';
END;

Output:

 SELECT * FROM ANALYSIS_QUERIES;
DIR_NAME FILE_NAME THE_SQL RESULT
COUNT_CHECK USER_COUNT_CHECK.sql select count(*) as usr_ct from users; 125

Approach 2:

> I've tried
>
> set stmt = (select the_sql from analysis_queries);
> execute immediate $stmt;

Using Snowflake Scripting block:

BEGIN 
   LET THE_SQL TEXT := (SELECT THE_SQL 
                        FROM ANALYSIS_QUERIES 
                        WHERE FILE_NAME = 'USER_COUNT_CHECK.sql');

   LET RES RESULTSET := (EXECUTE IMMEDIATE :THE_SQL);

   RETURN TABLE(RES);
END;

Output:

USR_CT
125

Related: Using Snowflake Scripting in SnowSQL and the Classic Console

huangapple
  • 本文由 发表于 2023年5月6日 22:33:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76189452.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定