使用Druid的sql parser做一个表数据血缘分析工具

2024-06-02 16:48

本文主要是介绍使用Druid的sql parser做一个表数据血缘分析工具,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


版权
前言
大数据场景下,每天可能都要在离线集群,运行大量的任务来支持业务、运营的分析查询。任务越来越多的时候,就会有越来越多的依赖关系,每一个任务都需要等需要的input表生产出来后,再去生产自己的output表。最开始的时候,依赖关系自然是可以通过管理员来管理,随着任务量的加大,就需要一个分析工具来解析任务的inputs、outs,并且自行依赖上生产inputs表的那些任务。本文就介绍一个使用druid parser,来解析SQL的input、output的血缘分析工具。

建议对druid比较陌生的同学可以先看下druid的官方文档。

做一次sql的血缘分析的流程
解析sql,拿到抽象语法树
遍历抽象语法树,得到from、to
使用druid解析sql到语法树
druid提供了简单、快速的SQL解析工具,可以很简单拿到一段SQL的AST(抽象语法树)。而druid对语法树提供了多种的SQLStatement,使遍历语法树更加容易。

 SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, JdbcConstants.HIVE);
 SQLStatement stmt= parser.parseStatementList().get(0);
1
2
从语法树中取出from和to
拿到语法树之后,想办法把from、to从语法树中取出来就大功告成。

最初的写法
最开始,就是简单的遍历一下语法树的节点,取出from表和to表的表名。

    /**
     * 根据create或者insert的sql取出from、to
     * @param sql
     * @return
     * @throws ParserException
     */
    private static Map<String, Set<String>> getFromTo(String sql) throws ParserException {
        SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, JdbcConstants.HIVE);
        SQLStatement stmt= parser.parseStatementList().get(0);

        Set<String> from = new HashSet<>();
        Set<String> to = new HashSet<>();
        if (stmt instanceof SQLInsertStatement) {
            SQLInsertStatement istmt = (SQLInsertStatement) stmt;
            to.add(istmt.getTableSource().toString().toUpperCase());

            SQLTableSource sts = istmt.getQuery().getQueryBlock().getFrom();
            from = getFromTableFromTableSource(sts);
        } else if (stmt instanceof SQLCreateTableStatement) {
            SQLCreateTableStatement cstmt = (SQLCreateTableStatement) stmt;
            to.add(cstmt.getTableSource().toString().toUpperCase());

            SQLTableSource sts = cstmt.getSelect().getQueryBlock().getFrom();
            from = getFromTableFromTableSource(sts);
        }

        Map<String, Set<String>> fromTo = new HashMap<>(4);
        fromTo.put("from", from);
        fromTo.put("to", to);
        return fromTo;
    }

    private static Set<String> getFromTableFromTableSource (SQLTableSource sts) {
        Set<String> from = new HashSet<>();
        if (sts instanceof SQLJoinTableSource) {
            from = getFromTableFromJoinSource((SQLJoinTableSource)sts);
        } else {
            from.add(sts.toString().toUpperCase());
        }
        return from;
    }

    private static Set<String> getFromTableFromJoinSource (SQLJoinTableSource sjts) {
        Set<String> result = new HashSet<>();
        getFromTable(result, sjts);
        return result;
    }

    // 递归获取join的表list
    private static void getFromTable (Set<String> fromList, SQLJoinTableSource sjts) {
        SQLTableSource left = sjts.getLeft();
        if (left instanceof SQLJoinTableSource) {
            getFromTable(fromList, (SQLJoinTableSource)left);
        } else {
            fromList.add(left.toString().toUpperCase());
        }
        SQLTableSource right = sjts.getRight();
        if (right instanceof SQLJoinTableSource) {
            getFromTable(fromList, (SQLJoinTableSource)right);
        } else {
            fromList.add(right.toString().toUpperCase());
        }
    }

用druid更好的实现
因为是为了快速完成,所以写的取出from、to表的部分还是存在很大的问题的。只能支持一条sql,只能支持简单的sql语句,比如union all或者子查询就有些无力。于是又看了一下文档,其实druid是提供了visitor方法来遍历语法树的,而且提供了一个简单的SchemaStatVisitor,可以取出Sql中所有用到的表。于是就可以写成这种格式。

public static Map<String, TreeSet<String>> getFromTo (String sql) throws ParserException {
        List<SQLStatement> stmts = SQLUtils.parseStatements(sql, JdbcConstants.HIVE);
        TreeSet<String> fromSet = new TreeSet<>();
        TreeSet<String> toSet = new TreeSet<>();
        if (stmts == null) {
            return null;
        }

        String database="DEFAULT";
        for (SQLStatement stmt : stmts) {
            SchemaStatVisitor statVisitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.HIVE);
            if (stmt instanceof SQLUseStatement) {
                database = ((SQLUseStatement) stmt).getDatabase().getSimpleName().toUpperCase();
            }
            stmt.accept(statVisitor);
            Map<Name, TableStat> tables = statVisitor.getTables();
            if (tables != null) {
                final String db = database;
                tables.forEach((tableName, stat) -> {
                    if (stat.getCreateCount() > 0 || stat.getInsertCount() > 0) {
                        String to = tableName.getName().toUpperCase();
                        if (!to.contains("."))
                            to = db + "." + to;
                        toSet.add(to);
                    } else if (stat.getSelectCount() > 0) {
                        String from = tableName.getName().toUpperCase();
                        if (!from.contains("."))
                            from = db + "." + from;
                        fromSet.add(from);
                    }
                });
            }
        }
 

这篇关于使用Druid的sql parser做一个表数据血缘分析工具的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1024535

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3