当前位置: 首页 > article >正文

血缘解析<二>:如何解析带CTE语句的Sql

blood

    • 一、思路
    • 二、代码

一、思路

之前文章血缘解析介绍了血缘解析的思路,但是对于带CTE语句的sql解析不到
eg:

with tmp as (
	select id,name,age
	  from personinfo
),tmp1 as (
	select a.id,a.name,a.age,b.classno
	  from tmp a
	  join classinfo b
	    on a.id = b.id
)
	select id,name,age,classno from tmp1

上述sql在解析的时候会获取到源表是tmp1,而不是cte中的personinfo和classinfo,原因是:com.alibaba.druid.sql获取getSelect().getQuery()只能获取到不包含cte的select语句,如下所示

        try{
            statements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
        }catch (Exception e){
            System.out.println("can't parser by druid oracle"+e);
        }
        // 只考虑一条语句
        SQLStatement statement = statements.get(0);
        // 只考虑查询语句
        SQLSelectStatement sqlSelectStatement = (SQLSelectStatement) statement;
        SQLSelectQuery sqlSelectQuery = sqlSelectStatement.getSelect().getQuery();

后面我想到了一种思路:带with的cte语句实际上也是临时表,tmp是临时表的表名,包含的select语句是临时表名的加工语句,可以把所有cte中的select语句都拿出来解析,最后通过递归获取最原始的血缘关系,对应关系如下所示:

  • 假设上述加工语句是写入test表
  • 第一层:
target_tabtarget_colsource_tabsource_col
testidtmp1id
testnametmp1name
testagetmp1age
testclassnotmp1classno
  • 第二层:
target_tabtarget_colsource_tabsource_col
tmp1idtmpid
tmp1nametmpname
tmp1agetmpage
tmp1classnoclassinfoclassno
  • 第三层:
target_tabtarget_colsource_tabsource_col
tmpidpersoninfoid
tmpnamepersoninfoname
tmpagepersoninfoage

会把上述所有层的对应关系都写到Mysql的表中,最后会利用Mysql的递归获取最原始的对应关系,递归代码如下所示:

with recursive cte (target_tab,target_col,source_tab,source_col,level) as 
(
	select target_tab,target_col,source_tab,source_col,1 as level
	  from blood_tab
	 union all
	select t1.target_tab,t1.target_col,t.source_tab,t.source_col,t1.level + 1
	  from blood_tab t
	  join cte1 t1
	    on t.target_tab = t1.source_tab
	   and t.target_col = t1.source_col
),cte1 as (
	select target_tab,target_col,source_tab,source_col,
	       rank()over(partition by target_tab,target_col order by level desc) as rn
	  from cte
)
	select target_tab,
	       target_col,
	       source_tab,
	       source_col
	  from cte1 t
	 where t.rn <= 1

二、代码

  • 如何获取所有cte的别名和包含的select语句?,可以用正则表达式匹配或者其它方式,我这里还是利用com.alibaba.druid.sql包进行解析获取
package com.lan.lineage.operate;


import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.alibaba.druid.util.JdbcConstants;
import com.lan.lineage.common.EmptyUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;


public class CteSpilt {

    public static void splitcte(String sql, String target_tab, String target_schema) {
        String target_tab_with;
        String target_sql;
        String text;


        if ( EmptyUtils.isEmpty(sql)){
            return;
        }
        AtomicReference<Boolean> isContinue = new AtomicReference<>(false);
        List<SQLStatement> statements = new ArrayList<>();
        // 解析
        try{
            statements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
        }catch (Exception mysqlerr) {
            System.out.println("can't parser by druid mysql" + mysqlerr);
            try {
                statements = SQLUtils.parseStatements(sql, JdbcConstants.ORACLE);
            } catch (Exception oracleerr){
                System.out.println("can't parser by druid ORACLE" + oracleerr);
            }
        }

        try {
            SQLStatement statement = statements.get(0);
            SQLSelectStatement sqlSelectStatement = (SQLSelectStatement) statement;
            SQLWithSubqueryClause withSub = sqlSelectStatement.getSelect().getWithSubQuery();

            if (withSub != null) {
                List<SQLWithSubqueryClause.Entry> withSubEntry = withSub.getEntries();
                for (int i = 0; i < withSubEntry.size(); i++) {
                    target_tab_with = withSubEntry.get(i).getAlias();
                    target_sql = String.valueOf(withSubEntry.get(i).getSubQuery());
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }

    }
}
  • 上述代码的target_tab_with就是cte名,target_sql就是cte中包含的select语句

http://www.kler.cn/a/305528.html

相关文章:

  • Python如何从HTML提取img标签下的src属性
  • 介绍和安装及数据类型
  • 前端web
  • uniapp vue里按钮上的文字,换行的方法,用rich-text
  • CSS 色彩魔法:打造绚丽网页风格
  • Vue Cli的配置中configureWebpack和chainWebpack的主要作用及区别是什么?
  • [机器学习]决策树
  • Parallels Desktop 20 for Mac中文版发布了?会哪些新功能
  • 学习笔记-Golang中的Context
  • 基础算法(3)——二分
  • Java邮件:如何配置以实现自动化邮件通知?
  • 平安养老险阜阳中心支公司开展金融教育宣传专项活动
  • ElementUI 快速入门:使用 Vue 脚手架搭建项目
  • SQL 代表什么?SQL 的全称是什么?
  • 二叉树算法 JAVA
  • 微信小程序中的模块化、组件化开发:完整指南
  • 资源管理新视角:利用 FastAPI Lifespan 事件优化你的应用
  • Android Greendao的数据库复制到设备指定位置
  • PhpStudy下载安装使用学习
  • 外国车牌字符识别与分类系统源码分享
  • PPT幻灯片的添加与编辑:全面技术指南
  • 【30天玩转python】高级数据结构
  • 2024年增强现实(AR)的现状
  • 用牛只面部图像实现牛只身份识别(与人脸识别不同的牛脸识别)
  • 发展绿色新质生产力,创维汽车亮相2024国际数字能源展
  • SSHamble:一款针对SSH技术安全的研究与分析工具