Greenplum 函数内嵌套查询在query中调用的替代方案

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 背景 在使用数据库过程中,用户可能会封装一些函数,用来返回一些映射值。 例如,有一张表存储了学号和学生名字,写一个函数,根据学号,返回学生名字。 SQL如下 : create table tab(id int primary key, std_name name); cr

背景

在使用数据库过程中,用户可能会封装一些函数,用来返回一些映射值。

例如,有一张表存储了学号和学生名字,写一个函数,根据学号,返回学生名字。

screenshot

SQL如下 :

create table tab(id int primary key, std_name name);

create or replace function f1(int) returns name as 
$$

  select std_name from tab where id=$1;

$$
 language sql strict volatile;

dwh=> insert into tab values (1,'abc');
INSERT 0 1

dwh=> select f1(1);
 f1  
-----
 abc
(1 row)

使用Greenplum时,可能需要注意,因为如果函数中有QUERY,但是这个函数又在QUERY中调用的话,可能会报错。

目前执行计划还不支持这种查询:

dwh=> select f1(id) from tab;
ERROR:  XX000: function cannot execute on segment because it accesses relation "public.tab" (functions.c:155)  (seg0 slice1 e45f16674.cloud.nu17:3017 pid=20659) (cdbdisp.c:1326)
DETAIL:  SQL function "f1" during startup
LOCATION:  cdbdisp_finishCommand, cdbdisp.c:1326

出错原因,在检查segment节点query tree时,如果发现树中还有非catalog类的对象,则报错。
可能是master节点对这种QUERY的处理没有彻底。

/**
 * Walker for querytree_safe_for_segment.
 */
bool querytree_safe_for_segment_walker(Node *expr, void *context)
{
        Assert(context == NULL);

        if (!expr)
        {
                /**
                 * Do not end recursion just because we have reached one leaf node.
                 */
                return false;
        }

        switch(nodeTag(expr))
        {
                case T_Query:
                        {
                                Query *q = (Query *) expr;

                                if (!allow_segment_DML &&
                                        (q->commandType != CMD_SELECT
                                         || q->intoClause != NULL
                                         || q->resultRelation > 0))
                                {
                                        elog(ERROR, "function cannot execute on segment because it issues a non-SELECT statement");
                                }

                                ListCell * f = NULL;
                                foreach(f,q->rtable)
                                {
                                        RangeTblEntry *rte = (RangeTblEntry *) lfirst(f);

                                        if (rte->rtekind == RTE_RELATION)
                                        {
                                                Assert(rte->relid != InvalidOid);

                                                Oid namespaceId = get_rel_namespace(rte->relid);

                                                Assert(namespaceId != InvalidOid);

                                                if (!(IsSystemNamespace(namespaceId) ||
                                                          IsToastNamespace(namespaceId) ||
                                                          IsAoSegmentNamespace(namespaceId)))
                                                {
                                                        elog(ERROR, "function cannot execute on segment because it accesses relation \"%s.%s\"",
                                                                        quote_identifier(get_namespace_name(namespaceId)), quote_identifier(get_rel_name(rte->relid)));
                                                }
                                        }
                                }
                                query_tree_walker(q, querytree_safe_for_segment_walker, context, 0);  
...

使用with也不能解决这个问题,因为不会等收到master节点后再调用f1

dwh=> with t as (select * from tab) select f1(id) from t;
ERROR:  XX000: function cannot execute on segment because it accesses relation "public.tab" (functions.c:155)  (seg0 slice1 e45f16674.cloud.nu17:3017 pid=26324) (cdbdisp.c:1326)
DETAIL:  SQL function "f1" during startup
LOCATION:  cdbdisp_finishCommand, cdbdisp.c:1326

dwh=> with t as (select * from tab) select * from t;
 id | std_name 
----+----------
  1 | abc
  2 | abc
(2 rows)

聚合后还是不行
dwh=> with t as (select array_agg(id) ag from tab) select unnest(ag) from t;
 unnest 
--------
      2
      1
(2 rows)

dwh=> with t as (select array_agg(id) ag from tab) select f1(unnest(ag)) from t;
ERROR:  XX000: query plan with multiple segworker groups is not supported (cdbdisp.c:302)
HINT:  likely caused by a function that reads or modifies data in a distributed table
CONTEXT:  SQL function "f1" statement 1
LOCATION:  cdbdisp_dispatchToGang, cdbdisp.c:302

怎么解决这个问题呢?
可以用子查询(括号中的部分)替代 function 。

dwh=> select *, (select std_name from tab where tab.id=t.id) from tab t;
 id | std_name | ?column? 
----+----------+----------
  1 | abc      | abc
(1 row)
目录
相关文章
|
4月前
|
SQL
SQL开发问题之当从数据源读取多个字段时优化 COUNT(DISTINCT ...) 的查询的问题如何解决
SQL开发问题之当从数据源读取多个字段时优化 COUNT(DISTINCT ...) 的查询的问题如何解决
|
4月前
|
SQL 分布式计算 MaxCompute
SQL开发问题之对于ODPS中的UNION操作,执行计划的问题如何解决
SQL开发问题之对于ODPS中的UNION操作,执行计划的问题如何解决
|
6月前
|
SQL 缓存 关系型数据库
一次sql改写优化子查询的案例
在生产环境中,一个MySQL RDS实例遭遇了高CPU使用率问题,原因是执行了一条复杂的UPDATE SQL语句,该语句涉及一个无法缓存的子查询(UNCACHEABLE SUBQUERY),导致子查询需要针对每一行数据重复执行,极大地影响了性能。SQL语句的目标是更新一行数据,但执行时间长达30秒。优化方法是将子查询转换为内连接形式,优化后的语句执行时间降低到毫秒级别,显著减少了CPU消耗。通过示例数据和执行计划对比,展示了优化前后的时间差异和执行效率的提升。
245 2
|
6月前
|
SQL
Greenplum【SQL 03】实现树结构+自定义函数+避免函数重复调用+ function cannot execute on a QE slice 问题处理(优化过程全记录)
Greenplum【SQL 03】实现树结构+自定义函数+避免函数重复调用+ function cannot execute on a QE slice 问题处理(优化过程全记录)
153 0
|
6月前
|
SQL 关系型数据库 数据库连接
Hasor【环境搭建 03】Dataway接口配置服务使用DataQL聚合查询引擎(SQL执行器实现分页查询举例说明+报错 Query dialect missing 原因分析及解决)
Hasor【环境搭建 03】Dataway接口配置服务使用DataQL聚合查询引擎(SQL执行器实现分页查询举例说明+报错 Query dialect missing 原因分析及解决)
145 0
|
SQL 分布式计算 大数据
分享一个 HIVE SQL 性能优化点-使用公共表表达式 CTE 替换临时表
分享一个 HIVE SQL 性能优化点-使用公共表表达式 CTE 替换临时表
|
6月前
|
SQL 缓存 Java
SQL Query Plan在Presto中的缓存设计与实现
阿里云日志服务(SLS)提供一站式数据采集、加工、查询分析、告警、可视化与投递等功能,其中查询分析以简单统一的接口提供大规模数据的查询、计算和分析能力,深受用户喜爱。 目前,分析系统每天接收5+亿次SQL查询请求,在底层,分析系统基于Presto内核,其中Coordinator节点上负载尤其严重,其...
259 0
SQL Query Plan在Presto中的缓存设计与实现
|
SQL
一张图看懂 SQL 的各种 join 用法!
一张图看懂 SQL 的各种 join 用法!
307 0
一张图看懂 SQL 的各种 join 用法!
|
SQL
SQL 中的聚集函数?
SQL 中的聚集函数?
145 0
SQL 中的聚集函数?
|
SQL 设计模式 消息中间件
如何实现一个跨库连表SQL生成器?
用户只需在前端简单配置下指标,系统即可自动生成大宽表,让用户查询到他所需要的实时数据,数据源支持跨库并支持多种目标介质。这样的数据全局实时可视化如何实现?本文从需求分析开始,分享自动生成SQL功能开发中运用到的设计模式和数据结构算法设计。
3433 0
如何实现一个跨库连表SQL生成器?