Snowflake编译器错误的解决方法

问题描述

有时,SNowflake sql编译器出于自身利益而试图变得过于聪明。这是previous question here的后续工作,在{{3}}中,为我的给定用例提供了一个聪明的解决方案,但该解决方案遇到了一些限制。

简要背景;我有一个JS-UDTF,它需要3个float参数来返回表示一系列<div class="none" id="search">Example</div> <button class="show-btn fas" id="show">Show</button>的行,还有一个sql-UDTF GENERATE_SERIES(FLOAT,FLOAT,FLOAT),它将参数转换为floats,然后调用JS-UDTF,然后返回结果回到整数。此包装UDTF的原始版本为:

GENERATE_SERIES(INT,INT,INT)

在输入不是常量的大多数情况下会失败,例如:

CREATE OR REPLACE FUNCTION generate_series(FirsT_VALUE INTEGER,LAST_VALUE INTEGER,STEP_VALUE INTEGER)
RETURNS TABLE (GS_VALUE INTEGER)
AS
$$
SELECT GS_VALUE::INTEGER AS GS_VALUE FROM table(generate_series(FirsT_VALUE::DOUBLE,LAST_VALUE::DOUBLE,STEP_VALUE::DOUBLE))
$$;

会返回错误

WITH report_params AS (
  SELECT
    1::integer as first_value,3::integer as last_value,1::integer AS step_value
)      
SELECT
  *
FROM
    report_params,table(
  generate_series(
    first_value,last_value,step_value
  )
)

提供的欺骗sql编译器行为的解决方案是将函数参数封装到VALUES表中并交叉联接内部UDTF:

sql compilation error: Unsupported subquery type cannot be evaluated

对于大多数调用而言,这很不错,但是我发现sql编译器再次出现这种情况。这是一个重现该问题的简化示例:

CREATE OR REPLACE FUNCTION generate_series_int(FirsT_VALUE INTEGER,STEP_VALUE INTEGER)
    RETURNS TABLE (GS_VALUE INTEGER)
    AS
$$
    SELECT GS_VALUE::INTEGER AS GS_VALUE 
    FROM (VALUES (first_value,step_value)),table(generate_series(first_value::double,last_value::double,step_value::double))
$$;

这会导致错误

WITH report_params AS (
  SELECT
    1::integer AS first_value,DATEDIFF('DAY','2020-01-01'::date,'2020-02-01'::date)::integer AS last_value,1::integer AS step_value
)      
SELECT
  *
FROM
  report_params,table(
  COMMON.FN.generate_series(
    first_value,step_value
  )
);

错误似乎很明显(我认为),编译器正在尝试将函数代码嵌入到外部查询中,这些外部查询在运行时将函数像宏一样对待。

这时的答案可能只是我对SNowflake的当前功能要求不高,但是出于学习和继续构建我认为非常有用的UDF库的兴趣,我想知道是否有一个解决方案,我很想念。

解决方法

主要问题是您编写了一个相关的子查询。

WITH report_params AS (
  SELECT * FROM VALUES
    (1,30,1)
    v(first_value,last_value,step_value) 
)      
SELECT
  *
FROM
  report_params,table(
  COMMON.FN.generate_series(
    first_value,step_value
  )
);

就像您在CTE中添加第二行一样

WITH report_params AS (
  SELECT * FROM VALUES
    (1,1),(2,40,2)
    v(first_value,step_value
  )
);

这很明显是相互关联的,不是很明显雪花应该由谁来执行。

对于上述数据,理想的外观是(如果它是有效的SQL)

WITH report_params AS (
   SELECT *,mod(v.first_value,v.step_value) as mod_offset
      FROM VALUES
        (0,5,20,(1,3,15,3),4,(3,3)
        v(id,first_value,step_value) 
),report_ranges AS (
  SELECT min(first_value) as mmin,max(last_value) as mmax
  FROM report_params
  WHERE first_value <= last_value AND step_value > 0
),all_range AS (
  SELECT 
   row_number() over (order by seq8()) + rr.mmin - 1 as seq
  FROM report_ranges rr,TABLE(GENERATOR( ROWCOUNT => (rr.mmax - rr.mmin) + 1 ))
)
SELECT 
  ar.seq,rp.id,rp.first_value,rp.last_value,rp.step_value,rp.mod_offset
FROM all_range as ar
JOIN report_params as rp ON ar.seq BETWEEN rp.first_value AND rp.last_value AND mod(ar.seq,rp.step_value) = rp.mod_offset
ORDER BY 2,1;

但是如果您在存储过程(或外部)中生成它,则可以替换为

 WITH report_params AS (
   SELECT *,all_range AS (
  SELECT 
   row_number() over (order by seq8()) + 3 /*min*/ - 1 as seq
  FROM TABLE(GENERATOR( ROWCOUNT => (20/*max*/ - 3/*min*/) + 1 ))
)
SELECT 
  ar.seq,1;

给予:

SEQ ID  FIRST_VALUE LAST_VALUE  STEP_VALUE  MOD_OFFSET
5   0   5   20  1   0
6   0   5   20  1   0
7   0   5   20  1   0
8   0   5   20  1   0
9   0   5   20  1   0
10  0   5   20  1   0
11  0   5   20  1   0
12  0   5   20  1   0
13  0   5   20  1   0
14  0   5   20  1   0
15  0   5   20  1   0
16  0   5   20  1   0
17  0   5   20  1   0
18  0   5   20  1   0
19  0   5   20  1   0
20  0   5   20  1   0
3   1   3   15  3   0
6   1   3   15  3   0
9   1   3   15  3   0
12  1   3   15  3   0
15  1   3   15  3   0
4   2   4   15  3   1
7   2   4   15  3   1
10  2   4   15  3   1
13  2   4   15  3   1
5   3   5   15  3   2
8   3   5   15  3   2
11  3   5   15  3   2
14  3   5   15  3   2

我无法猜测的问题,是感觉像您以太试图隐藏表函数JS函数背后的某些复杂性,还是出于未知原因使事情变得复杂。

[编辑1-9评论] generate_seriesGENERATOR之间的主要区别在于前者几乎是UDF或CTE,而在雪花中,您必须在自己的子选择中使用GENERATOR,否则结果会很混乱。 >

 with s1 as (
  SELECT 
    row_number() over (order by seq8()) -1 as seq
  FROM 
  TABLE(GENERATOR( ROWCOUNT => 3 ))
),s2 as (
  SELECT 
    row_number() over (order by seq8()) -1 as seq
  FROM 
  TABLE(GENERATOR( ROWCOUNT => 3 ))
)
select s1.seq as a,s2.seq as b
from s1,s2
order by 1,2;

将两个数据的9行混合在一起,就像您不想那样。 在哪里

with s1 as (
  SELECT 
    row_number() over (order by seq8()) -1 as seq
  FROM 
  TABLE(GENERATOR( ROWCOUNT => 3 ))
)
SELECT 
  row_number() over (order by seq8()) -1 as a,s1.seq as b
FROM 
TABLE(GENERATOR( ROWCOUNT => 3 )),s1;

给出1-9,因为在序列代码运行之前,GENERATOR(行的创建者)已与其他数据交叉。

提供的原始解决方案的另一个版本是

WITH report_params AS (
   SELECT *,trunc(div0((last_value-first_value),step_value)) as steps
      FROM VALUES
        (0,large_range AS (
  SELECT 
   row_number() over (order by seq8()) -1 as seq
  FROM 
  TABLE(GENERATOR( ROWCOUNT => 1000 ))
)
select rp.id,rp.first_value + (lr.seq*rp.step_value) as val
from report_params as rp
join large_range as lr on lr.seq <= rp.steps
order by 1,2;

我更喜欢它,因为混合的性质更加清晰。但这仍然说明了雪花和其他RDB之间的思维差异。在postgress中,进行单行操作没有任何成本,因为它是一个完全按行进行操作的时代,但是雪花没有按行的选择,并且因为它不能在每一行上执行任何操作,所以它可以独立执行许多行。这意味着每行的所有表达式都需要移到最前面,然后再加入。因此,上面试图显示的内容。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...