以数组为参数的雪花函数因不支持的子查询错误而失败

Posted

技术标签:

【中文标题】以数组为参数的雪花函数因不支持的子查询错误而失败【英文标题】:Snowflake function with Array as parameter fails with Unsupported subquery error 【发布时间】:2020-04-09 15:52:19 【问题描述】:

我有一个事务表,我需要对相似的记录进行分组,对于可能唯一的类列,我需要从查找表(类表)中选择相似记录的值中的最高值(循环表根据优先级排序)。

从类中选择 *;

ID  NAME
2   BETA
6   OMEGA
5   SIGMA
1   ALPHA1
3   GAMMA
4   DELTA
CREATE OR REPLACE FUNCTION "MIN_VALUE"(classlist array)
RETURNS VARCHAR(200)
LANGUAGE SQL
AS '
    select NAME from CLASS 
    where ID in ( select min(ID) from CLASS 
                               where NAME in (select value from table(flatten(input=>classlist))))
';

从 T_DATA 中选择 *;

C_ID    P_ID    D_ID    S_ID    CLASS
1101111 1404    564     1404    BETA
1101111 1404    599     1425    ALPHA
1101111 1404    564     1404    OMEGA
1101111 1404    564     1425    ALPHA
1101111 1404    564     1404    GAMMA
1101111 1404    564     1425    GAMMA
1101111 1404    599     1425    GAMMA
1101111 1404    564     1425    OMEGA

当我编写如下查询时,它可以工作FINE

选择 MIN_VALUE(array_construct('OMEGA','GAMMA','BETA'));

当我在实际查询中使用它时,它会因 SQL 编译错误:无法评估不支持的子查询类型

而失败
select C_ID, P_ID, D_ID, S_ID, MIN_VALUE(class_array) from (
    select C_ID, P_ID, D_ID, S_ID, arrayagg(class) class_array
    from t_data 
    group by C_ID,P_ID,D_ID,S_ID
);

OR

select C_ID,P_ID,D_ID,S_ID,MIN_VALUE(ca) from (
    select C_ID,P_ID,D_ID,S_ID,array_construct(class_array) ca from (
        select C_ID,P_ID,D_ID,S_ID,arrayagg(class) class_array
        from t_data 
        group by C_ID,P_ID,D_ID,S_ID
    )
);

我期待上面 8 条记录的输出如下所示

select C_ID,P_ID,D_ID,S_ID,array_construct(class_array) ca from (
        select C_ID,P_ID,D_ID,S_ID,arrayagg(class) class_array
        from t_data 
        group by C_ID,P_ID,D_ID,S_ID
    );

Output

C_ID    P_ID    D_ID    S_ID    CLASS_ARRAY
1101111 1404    564     1404    ["OMEGA", "GAMMA", "BETA"]
1101111 1404    599     1425    ["ALPHA", "GAMMA"]
1101111 1404    564     1425    ["ALPHA", "GAMMA", "OMEGA"]

When I use the min_value function on the above class_array that will return a single value based on the priority in the lookup table.

C_ID    P_ID    D_ID    S_ID    CLASS_ARRAY
1101111 1404    564     1404    BETA
1101111 1404    599     1425    ALPHA
1101111 1404    564     1425    ALPHA

请提出一些选项来弄清楚为什么函数对于硬编码值运行良好但如果在查询中构造数组并作为参数传递则失败。

【问题讨论】:

【参考方案1】:

Snowflake 在支持包含某些 SELECT 模式列定义中的 SQL 语句方面存在一些限制。有几种方法可以重写上面的查询以获得所需的结果:

1) 找到最小ID,然后加入到类表中:

with T as (  
  select C_ID, P_ID, D_ID, S_ID, min(class.id) minclassid
  from t_data join class
     on class.name = t_data.class
  group by C_ID,P_ID,D_ID,S_ID
)
select C_ID, P_ID, D_ID, S_ID, class.name
from T join CLASS on minclassid = class.id;

2) 或者使用 Windowing 函数来获取组内按 ID 排序的第一个类名:

select distinct C_ID, P_ID, D_ID, S_ID, 
   first_value(class.name) over 
     (partition by C_ID, P_ID, D_ID, S_ID order by class.id) name
from t_data join class
on class.name = t_data.class;

【讨论】:

感谢斯图尔特的建议。我似乎没有明确提及的一件事是,在为每一行创建的数组中的值列表中,我需要在查找中选择至少一个(循环表是根据优先级排序的)。我已经更新了帖子。【参考方案2】:

这也可以使用QUALIFY 过滤器来完成,它允许在选择阶段之后进行过滤,并且该过滤器逻辑不会显示在结果中。

with class as (
    select * from values
      (2, 'BETA'),
      (6, 'OMEGA'),
      (5, 'SIGMA'),
      (1, 'ALPHA'),
      (3, 'GAMMA'),
      (4, 'DELTA')  
      v(id, name)
), t_data as (
    select * from values
      (1101111, 1404, 564, 1404, 'BETA'),
      (1101111, 1404, 599, 1425, 'ALPHA'),
      (1101111, 1404, 564, 1404, 'OMEGA'),
      (1101111, 1404, 564, 1425, 'ALPHA'),
      (1101111, 1404, 564, 1404, 'GAMMA'),
      (1101111, 1404, 564, 1425, 'GAMMA'),
      (1101111, 1404, 599, 1425, 'GAMMA'),
      (1101111, 1404, 564, 1425, 'OMEGA')
      v(C_ID, P_ID, D_ID, S_ID, CLASS)
)
select c_id, p_id, d_id, s_id, d.class
from t_data d
join class c on d.class = c.name
qualify row_number() over (partition by c_id, p_id, d_id, s_id order by c.id) = 1;

给予:

C_ID    P_ID    D_ID    S_ID    CLASS
1101111 1404    564     1404    BETA
1101111 1404    564     1425    ALPHA
1101111 1404    599     1425    ALPHA

这与更明确/详细的形式相同:

select c_id, p_id, d_id, s_id, class from (
    select c_id, p_id, d_id, s_id, d.class
        ,row_number() over (partition by c_id, p_id, d_id, s_id order by c.id) as rn
    from t_data d
    join class c on d.class = c.name
)
where rn = 1;

这与 Stuart 的 DISTINCT 的机制完全相同

如果您真的想通过数组来完成,您可以使用WITHIN GROUP (ORDER BY ..) 对数组进行排序,然后您可以获取第一个对象,但 FIRST_VALUE 或 QUALIFY 方法应该更快.. 但如果有其他原因保留数组,这可能会有所帮助

select C_ID, P_ID, D_ID, S_ID, class_array[0] ca from (
    select C_ID, P_ID, D_ID, S_ID, arrayagg(class) within group (order by class.id) class_array
    from t_data
    join class on t_data.class = class.name
    group by C_ID,P_ID,D_ID,S_ID
);

【讨论】:

谢谢西蒙!!质量选项完美运行。我需要在查找中为每一行的值列表选择最少的一个(循环表是根据优先级排序的)。限定确实给出了正确的结果。

以上是关于以数组为参数的雪花函数因不支持的子查询错误而失败的主要内容,如果未能解决你的问题,请参考以下文章

使用函数时雪花不支持的子查询

替换雪花中的子查询

雪花:无法评估不受支持的子查询类型

雪花中不支持的子查询

角度动画因不透明度​​设置而失败(以及更多错误)

雪花标量 UDF 返回 无法评估不支持的子查询类型