您当前的位置:首页 > 电脑百科 > 数据库 > 百科

使用 Calcite 解析 SQL 获取源表和结果表

时间:2022-08-30 14:31:06  来源:今日头条  作者:SapphireCoder

导读:本文将讨论关于使用 Calcite 解析单条 SQL 获取源表和结果表的思路。

实现思路

Apache Calcite是一款开源的动态数据管理框架,它提供了标准的SQL语言、多种查询优化和连接各种数据源的能力,但不包括数据存储、处理数据的算法和存储元数据的存储库。

 

我们可以借助 Calcite SqlParser 解析器分析 SQL 并生成 AST 语法树,并通过访问 AST 的各个节点获取到我们想要的信息。

具体实现

构建 Calcite SqlParser 解析器解析 SQL 并生成 AST 语法树

 @Test
    void test() {
        String sql = "insert into ... select ...";
        // 在解析前可以对 SQL 语句进行预处理,比如将不支持的 && 替换为 AND, != 替换为 <>
        SqlParser.Config config =
                SqlParser.configBuilder()
                        .setParserFactory(FlinkSqlParserImpl.FACTORY)
                        .setLex(Lex.JAVA)
                        .setIdentifierMaxLength(256)
                        .build();
        // 创建解析器
        SqlParser sqlParser = SqlParser
                .create(sql, config);
        // 生成 AST 语法树
        SqlNode sqlNode;
        try {
            sqlNode = sqlParser.parseStmt();
        } catch (SqlParseException e) {
            throw new RuntimeException("使用 Calcite 进行语法分析发生了异常", e);
        }
      	SqlBloodRes res = new SqlBloodRes();
        // 递归遍历语法树
        getDependencies(sqlNode, res, false);
    }

此处笔者参考了 从 SQL 语句中解析出源表和结果表 - JR's Blog 所提供的思路。在 Calcite 解析出来的 AST 是以 SqlNode 的形式表现的,一个 SqlNode 即是 AST 中的一个节点。SqlNode 有许多类型,我们关注的 Source 和 Sink 表表名在 AST 中会是一个 SqlIdentifier 的叶子结点。(注意:并非所有 SqlIdentifier 叶子结点都对应表名,列名也对应 SqlIdentifier)

在一条 SQL 中,最终出现表的引用的情况归结于以下两种情况:

  • SELECT 语句的 FROM clause 中的直接引用
  • JOIN 语句中 LEFT 和 RIGHT clause 中的直接引用

嵌套子查询的 SQL 语句中,最终进入到子查询的 AST 子树中,只要出现了对表的引用,一定会分解出以上两种结构。因此,对于一个 SqlIdentifier 类型的叶子节点,在以下两种情况下,该叶子结点就是一个表的引用:

  • 父节点是 SqlSelect,且当前节点是父节点的 FROM 子句派生出的子节点
  • 父节点是 SqlJoin(如果是 Lookup join 则节点为 SNAPSHOT 类型,需继续深入子节点)

另外,一种特殊的情况需要加以考虑。在 SQL 中 AS 常用作起别名,因而可能 SqlIdentifier 的父节点是 AS,而 AS 的父节点是 SELECT 或 JOIN。这种情况下,我们可以将 AS 看作一种 “转发” 结点,即 AS 的父节点和子节点忽略掉 AS 结点,直接构成父子关系。

从根结点开始遍历 AST,解析所有的子查询,找到符合上述两种情况的子结构,就可以提取出所有对表的引用。

private SqlBloodRes getDependencies(SqlNode sqlNode, SqlBloodRes res, Boolean fromOrJoin) {
        if (sqlNode.getKind() == JOIN) {
            SqlJoin sqlKind = (SqlJoin) sqlNode;
            getDependencies(sqlKind.getLeft(), res, true);
            getDependencies(sqlKind.getRight(), res, true);
        } else if (sqlNode.getKind() == IDENTIFIER) {
            if (fromOrJoin) {
                // 获取 source 表名
                res.getSourceTables().put(sqlNode.toString(), sqlNode.toString());
            }
        } else if (sqlNode.getKind() == AS) {
            SqlBasicCall sqlKind = (SqlBasicCall) sqlNode;
            if (sqlKind.getOperandList().size() >= 2) {
                getDependencies(sqlKind.getOperandList().get(0), res, fromOrJoin);
            }
        } else if (sqlNode.getKind() == INSERT) {
            SqlInsert sqlKind = (SqlInsert) sqlNode;
            // 获取 sink 表名
            res.setSinkTable(sqlKind.getTargetTable().toString());
            getDependencies(sqlKind.getSource(), res, false);
        } else if (sqlNode.getKind() == SELECT) {
            SqlSelect sqlKind = (SqlSelect) sqlNode;
            List<SqlNode> list = sqlKind.getSelectList().getList();
            for (SqlNode i : list) {
                getDependencies(i, res, false);
            }
            getDependencies(sqlKind.getFrom(), res, true);
        } else if (sqlNode.getKind() == SNAPSHOT) {
            // 处理 Lookup join 的情况
            SqlSnapshot sqlKind = (SqlSnapshot) sqlNode;
            getDependencies(sqlKind.getTableRef(), res, true);
        }   else {
            // TODO 这里可根据需求拓展处理其他类型的 sqlNode
        }
        return res;
    }

结果封装

@Data
public class SqlBloodRes {

    private Map<String, String> sourceTables = new HashMap<>();

    private String sinkTable;
}

最后

上面就是使用 Calcite 解析 SQL 获取源表和结果表的思路,Demo 实现比较粗糙,各位可以根据实际场景及自身需求进行优化丰富。



Tags:Calcite   点击:( )  评论:( )
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:[email protected]),我们将及时更正、删除,谢谢。
▌相关推荐
导读:本文将讨论关于使用 Calcite 解析单条 SQL 获取源表和结果表的思路。实现思路Apache Calcite是一款开源的动态数据管理框架,它提供了标准的SQL语言、多种查询优化和连接...【详细内容】
2022-08-30  Tags: Calcite  点击:(0)  评论:(0)  加入收藏
Knoldus Inc.3分钟阅读嘿那里,作为一个技术人员有时我们必须编写数据库的查询,看起来不错,但我们不知道我们写的查询是句法正确的。所以在这个博客中,我们在Apache Calcite的帮...【详细内容】
2021-02-24  Tags: Calcite  点击:(723)  评论:(0)  加入收藏
▌澳门威斯尼斯人app官方下载推荐
导读:本文将讨论关于使用 Calcite 解析单条 SQL 获取源表和结果表的思路。实现思路Apache Calcite是一款开源的动态数据管理框架,它提供了标准的SQL语言、多种查询优化和连接...【详细内容】
2022-08-30  SapphireCoder  今日头条  Tags:Calcite   点击:(0)  评论:(0)  加入收藏
开发中经常说到数据入库,我们在实现时需要知道使用何种技术,连接哪个数据库,连接数据库的账号和密码等等。今天要跟大家分享的是spring web项目中使用mybatis连接到postgresql...【详细内容】
2022-08-29  桥棠桐竹屋  今日头条  Tags:数据库   点击:(5)  评论:(0)  加入收藏
摘要:区别于PostgreSQL和Oracle,MariaDB采取的是一种现代的云原生的方法,在低成本、耐用的云存储上管理几乎无限量的地理空间数据,并提供基于OGC(开放地理空间信息联盟)标准的REST...【详细内容】
2022-08-29    CSDN  Tags:MariaDB   点击:(0)  评论:(0)  加入收藏
近期官网给出了RedisJson(RedisSearch)的性能测试报告,可谓碾压其他NoSQL,下面是核心的报告内容,先上结论: 对于隔离写入(isolated WRITEs),RedisJSON 比 Mongodb 快 5.4 倍,比 Elas...【详细内容】
2022-08-28  互联网资讯看板     Tags:RedisJson   点击:(17)  评论:(0)  加入收藏
分表分库实现思路技术选型这一难题解决后,具体如何落实分表分库方案呢?需要考虑5个要点。1)使用什么字段作为分片主键?2)分片的策略是什么?3)业务代码如何修改?4)历史数据如何迁移?5)未...【详细内容】
2022-08-26  互联共商   51CTO  Tags:分表分库   点击:(18)  评论:(0)  加入收藏
-------------------MongoDB数据导入与导出-------------------1、导出工具:mongoexport1、概念: mongoDB中的mongoexport工具可以把一个collection导出成JSON格式或CSV...【详细内容】
2022-08-22  GavinDjp    Tags:mongodb   点击:(19)  评论:(0)  加入收藏
一、背景介绍从系统设计角度看,一个系统从设计搭建到数据逐步增长,SQL 执行效率可能会出现劣化,为继续支撑业务发展,我们需要对慢 SQL 进行分析和优化,严峻的情况下甚至需要对整...【详细内容】
2022-08-17    字节跳动技术团队  Tags:慢SQL   点击:(18)  评论:(0)  加入收藏
小 T 导读:作为薪水较为可观的 IT 职业之一,DBA 貌似是一个门槛低、活还少的技术岗位,只要接受过相应的系统性训练,应该就可以成功入门。但想要真正胜任这个岗位,实际却并不容易...【详细内容】
2022-08-09  TDengine涛思数据  今日头条  Tags:DBA   点击:(1)  评论:(0)  加入收藏
一.问题描述我司在某云的MySQL数据库占硬盘空间大于90%,RDS空间总空间为 700G,表A分析之后。某渠道统计的表有5亿,单表空间超过350G。服务器架构:一主多从。 报警截图: 二.处理流...【详细内容】
2022-08-09  星锅说事    Tags:数据库   点击:(31)  评论:(0)  加入收藏
前言在实际的项目开发中,为了提高响应的速度,通常都会将热点的数据保存到缓存中,减少数据库的查询,有效提高服务端的响应速度,但是添加缓存之后也引入缓存与数据库的一致性问题,本...【详细内容】
2022-08-01  程序那点事    Tags:数据库   点击:(30)  评论:(0)  加入收藏
站内最新
站内热门
站内头条