纯文本分析方法,从复杂的sql查询语句中提取所有来源表名

如何从复杂的sql查询语句中提取所有来源表名

本文使用Python实现

一、背景

前段时间开发了一个小工具用来检测一些sql脚本,其中有一个步骤需要将查询语句的各个来源表都提取出来。本来借助数据库是可以实现的,但该方法有局限性。所以我一直在想,能不能通过纯文本分析的方法来解决这个问题。

网上虽然能找到类似的解决方法,但基本都不是通用的脚本,只能解决某些句式,考虑的场景太少了,想要找到通用的脚本,只能靠自己写了。

最后经过一番费力的分析拆解,终于写出来一个相对来说比较通用的脚本。

二、题外话

先说一下借助数据库可以怎么实现。
在oracle中,有个数据字典dba_dependences,可以查出某个对象(包、视图、表)所依赖的表和视图。假如我们想要借助数据库来实现这个功能,那么很简单,只要用sql查询语句先创建一个固定名字的视图,然后查询该视图的依赖的所有表,即可获得该查询语句的所有来源表。

但这种方式有2个弊端:
1、查询语句的来源表,必须是已经在数据库里建好的;
2、如果需要批量处理,频繁地连接数据库建视图然后查询依赖表,这个过程资源开销较大,中间需要等待的时间较长;

三、正题

那么如何通过文本分析的方式来解决这个问题呢?

1、先分析sql句式

分析日常我们编写的sql查询语句,大概有哪些句式,通过归纳总结,针对这些句式去分析如何按步骤拆解,简化,统一。

通过分析,可以把日常的sql查询语句归纳为以下的多种句式

.单表查询语句
--1、单表查询,没有过滤条件,没有分组条件
SELECT [列名]
FROM [表名]
--2、单表查询,有过滤条件,没有分组条件
SELECT [列名]
FROM [表名]
WHERE [过滤条件]
--3、单表查询,没有过滤条件,有分组条件
SELECT [列名]
FROM [表名]
GROUP BY [分组条件]
--4、单表查询,带子查询
SELECT [列名]
FROM ([子查询语句])
GROUP BY [分组条件].多表关联查询
--1、Oracle特有写法
SELECT [列名]
FROM [表名1],
    [表名2],
    [表名3]
WHERE [关联和过滤条件]
--2.1、sql标准写法
SELECT [列名]
FROM [表名1]
JOIN [表名2]
ON1.XX =2.XX
--2.2、sql标准写法2
SELECT [列名]
FROM [表名1]
INNER JOIN [表名2]
ON1.XX =2.XX
--2.3、sql标准写法3
SELECT [列名]
FROM [表名1]
LEFT JOIN [表名2]
ON1.XX =2.XX
--2.4、sql标准写法4
SELECT [列名]
FROM [表名1]
RIGHT JOIN [表名2]
ON1.XX =2.XX

三.多表关联查询嵌套子查询(上述所有情况把表名替换成子查询即可).WITH临时表写法
--1.1、WITH单临时表,单表查询,无过滤条件,无分组条件
WITH TMP AS (
子查询
)
SELECT [列名] FROM TMP;

--1.2、WITH单临时表,单表查询,有过滤条件,无分组条件
WITH TMP AS (
子查询
)
SELECT [列名] FROM TMP WHERE [过滤条件];

--1.3、WITH单临时表,单表查询,无过滤条件,有分组条件
WITH TMP AS (
子查询
)
SELECT [列名] FROM TMP GROUP BY [分组条件];

--2、WITH多临时表, 单表查询...
WITH TMP1 AS (
子查询1
),
TMP2 AS (
子查询2
)
SELECT [列名] FROM TMP1;
--3、WITH多临时表, 多表关联查询, ORACLE写法
WITH TMP1 AS (
子查询1
),
TMP2 AS (
子查询2
)
SELECT [列名]
FROM TMP1,
    TMP2
WHERE TMP1.XX = TMP2.XX(+)  --(+)可有可无
--4、WITH多临时表, 多表关联查询, 标准sql写法
WITH TMP1 AS (
子查询1
),
TMP2 AS (
子查询2
)
SELECT [列名]
FROM TMP1
JOIN TMP2   --JOIN 可能替换为LEFT JOIN , INNER JOIN , RIGHT JOIN
ON TMP1.XX = TMP2.XX(+)


2、拟定处理流程,绘制流程图

有了上面的句式,我们就可以思考如何按步骤来识别判断。
经过思考,上述的多种句式可以通过以下流程逐步拆解处理
在这里插入图片描述

3、开始编写代码

按照上面的流程来编写,细节处理自己多考虑一下,最终写出的代码如下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# date: 2021/6/6
# filename: get_tables_from_sql
# author: kplin

import re


def get_query_sql(file_path):
    '''读取文本中的sql'''
    with open(file_path, 'r', encoding='utf-8') as f:
        # 文本预处理:
        # 1、打开文件,获取文本内容,转换成大写,去除前后空格,去除换行符
        content = f.read().upper().strip(' ').split('\n')
        content_list = []
        for i in content:
            if not i.startswith('--') and i != '':
                # 2、去除单行注释
                j = i if '--' not in i else i.replace(i, i.split('--')[0])
                # 3、去除tab符
                content_list.append(j.replace('\t', ' '))
        # 4、双空格转为单空格
        content = ' '.join(content_list).replace('  ', ' ').replace('  ', ' ')
        # 5、去除多行注释
        comments = re.findall('/\*.*?\*/', content)
        for i in comments:
            content = content.replace(i, '')

        return content

def with_query(query_sql):
    '''传进一个with语句,返回临时表名,子查询和主查询'''
    # 先把(+) 替换成###++###, 避免干扰判断, 后面再替换回来,把双空格转换成单空格
    query_sql = query_sql.replace('(+)', '###++###').replace('  ', ' ').replace('  ', ' ')

    # 把AS (), 或者AS () , 或者AS ()SELECT 或者 AS () SELECT中间部分提取出来(这里分多个情况处理)
    tmp_querys = []
    tmp_querys1 = re.findall(r'AS \(.*?\),', query_sql)
    if len(tmp_querys1) != 0:
        for i in tmp_querys1:
            query_sql = query_sql.replace(i, '')

    tmp_querys2 = re.findall(r'AS \(.*?\) ,', query_sql)
    if len(tmp_querys2) != 0:
        for i in tmp_querys2:
            query_sql = query_sql.replace(i, '')

    tmp_querys3 = re.findall(r'AS \(.*?\) SELECT', query_sql)
    if len(tmp_querys3) != 0:
        for i in tmp_querys3:
            i = i[:-7]
            query_sql = query_sql.replace(i, '')
    tmp_querys4 = re.findall(r'AS \(.*?\)SELECT', query_sql)
    if len(tmp_querys4) != 0:
        for i in tmp_querys4:
            i = i[:-6]
            query_sql = query_sql.replace(i, '')

    tmp_querys5 = re.findall(r'AS\(.*?\),', query_sql)
    if len(tmp_querys5) != 0:
        for i in tmp_querys5:
            query_sql = query_sql.replace(i, '')

    tmp_querys6 = re.findall(r'AS\(.*?\) ,', query_sql)
    if len(tmp_querys6) != 0:
        for i in tmp_querys6:
            query_sql = query_sql.replace(i, '')

    tmp_querys7 = re.findall(r'AS\(.*?\) SELECT', query_sql)
    if len(tmp_querys7) != 0:
        for i in tmp_querys7:
            i = i[:-7]
            query_sql = query_sql.replace(i, '')

    tmp_querys8 = re.findall(r'AS\(.*?\)SELECT', query_sql)
    if len(tmp_querys8) != 0:
        for i in tmp_querys8:
            i = i[:-6]
            query_sql = query_sql.replace(i, '')


    for i in [tmp_querys1, tmp_querys2, tmp_querys3, tmp_querys4, tmp_querys5, tmp_querys6, tmp_querys7, tmp_querys8]:
        if len(i) != 0:
            tmp_querys.extend(i)
    # 把AS ()中间的子查询提取,获得临时表的查询语句
    sub_querys = []
    for i in tmp_querys:
        for j in re.findall(r'AS \((.*?)\),', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS \((.*?)\) ,', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS \((.*?)\) SELECT', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS \((.*?)\)SELECT', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))

        for j in re.findall(r'AS\((.*?)\),', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS\((.*?)\) ,', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS\((.*?)\) SELECT', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))
        for j in re.findall(r'AS\((.*?)\)SELECT', i):
            sub_querys.append(j.strip(' ').replace('###++###', '(+)').replace('  ', ' ').replace('  ', ' '))

    # 截取with和第一个select之间的部分,获取临时表名
    tmp_names = query_sql[query_sql.index('WITH') + 4 : query_sql.index('SELECT')].strip(' ').split(' ')
    tmp_names = [i for i in tmp_names if i != '']
    tmp_names = [i.strip(' ') for i in tmp_names]

    # 把剩下的select 到结尾部分截取出来,获得主查询语句
    main_query = query_sql[query_sql.index('SELECT'):].strip(' ').replace('  ', ' ').replace('  ', ' ').replace('###++###', '(+)')
    sub_querys.append(main_query)
    return (tmp_names, sub_querys)

def get_sub_query(query_sql):
    '''传进一条查询语句,返回主查询和所有子查询'''
    # 1、先去除(+)的干扰,去除前后空格,双空格替换为单空格
    query_sql = query_sql.replace('(+)', '###++###').strip(' ').replace('  ', ' ').replace('  ', ' ')
    # 2、匹配()内的子查询
    sub_querys = re.findall('\((.*?)\)', query_sql)
    sub_query_list = []
    if len(sub_querys) != 0:
        for i in sub_querys:
            if ' FROM ' in i:    # 括号内有FROM关键字才是子查询,不然函数可能也用括号包围
                query_sql = query_sql.replace(i,  '')
                i = i.replace('###++###', '(+)')
                sub_query_list.append(i)

    main_query = query_sql.replace('###++###', '(+)')
    sub_query_list.append(main_query)
    return sub_query_list

def split_join(ret_list):
    join_list = [' LEFT JOIN ', ' RIGHT JOIN ', ' OUTER JOIN ', ' JOIN ']
    for i in ret_list:
        for j in join_list:
            if j in i:
                ret_list.remove(i)
                ret_list.extend(i.split(j))
                return split_join(ret_list)
    return ret_list

def get_tables_from_sql(content):
        # 开始SQL解析流程
        with_query_dict = {'tmp_names': [], 'sub_querys': []}
        # 1、含有with关键字,则提取临时表查询语句和主查询语句
        querys = []
        if content.startswith('WITH'):
            # 处理with语句,取出临时表名,临时表查询语句,主查询语句(放在sub_querys的最后一个)
            tmp_names, sub_querys = with_query(content)
            # 把返回的结果记录在with_query_dict,方便后面取出判断是不是临时表名
            with_query_dict['tmp_names'] = tmp_names
            with_query_dict['sub_querys'] = sub_querys
            # 把前面获取到的临时表查询语句和主查询语句传进去,进一步拆分出其中嵌套的子查询语句, 把拆分出来的sql先暂存到querys里面
            for i in sub_querys:
                sub_query_list = get_sub_query(i)
                for j in sub_query_list:
                    querys.append(j)
        # 2、如果不是with写法,可以直接拿content继续处理,否则从query_dict中获取临时表别名和临时表查询语句,
        else:
            sub_query_list = get_sub_query(content)
            for j in sub_query_list:
                querys.append(j)

        # 3、到这里就全都是单个查询语句,不嵌套子查询了.
        # 3.1、先判断有没有join, left join, right join, outer join这些关键字,有的代表是标准Sql的多表关联
        sources_tables = []
        for i in querys:
            for j in [' LEFT JOIN ', ' RIGHT JOIN ', ' OUTER JOIN ', ' JOIN ']:
                if j in i:
                # 3.1.1、代表该表是标准sql的多表关联写法, 有可能会关联多次,各种组合都有可能,查到一个切割一次,对结果继续遍历
                # ,切割,直到最后没有了
                    ret = split_join([i])
                    # 获取到的ret第一个必然是有From的,并且From后是表名
                    if len(ret) > 0:
                        for x in range(len(ret)):
                            # 第一个肯定是带From的,取出from后的部分
                            if x == 0:
                                s = ret[x].strip(' ').replace('  ', ' ').replace('()', '').replace('( )', '')
                                table = s[s.index(' FROM ')+5:].strip(' ')
                                # 可能带有别名,尝试按空格切,切得到就取前面部分,切不到就是没有别名
                                try:
                                    tmp_list = table.split(' ')
                                    sources_tables.append(tmp_list[0].strip(' '))
                                except:
                                    sources_tables.append(table)
                            # 剩下的都是ON的,取出ON前面部分即可
                            else:
                                s = ret[x].strip(' ').replace('  ', ' ').replace('()', '').replace('( )', '')
                                table = s[:s.index(' ON ')].strip(' ')
                                # 可能带有别名,尝试按空格切,切得到就取前面部分,切不到就是没有别名
                                try:
                                    tmp_list = table.split(' ')
                                    sources_tables.append(tmp_list[0].strip(' '))
                                except:
                                    sources_tables.append(table)
                    # i 已经提取出源表名,直接从querys中移除
                    querys.remove(i)
                    # 注意检测到一个就交给split_join函数做循环切割,这里不再判断
                    break

            # 3.1.2、代表该语句不是join写法,要么是oracle关联写法,要么是单表查询语句
            if ' WHERE ' in i:
                # 3.1.2、获取FROM到WHERE之间的部分,有逗号代表是oracle多表关联
                table_str = i[i.index(' FROM ') + 5: i.index(' WHERE ')+1].strip(' ').replace('  ', ' ').\
                    replace('()', '').replace('( )', '')
                if ',' in table_str:
                    table_list = table_str.split(',')
                    for t in table_list:
                        t = t.strip(' ')
                        try:
                            tables = t.split(' ')
                            # 切割成功代表它是有别名的,取第一个作为真实表名
                            sources_tables.append(tables[0])
                        except:
                            # 切割不成功,代表它没有别名,直接添加
                            sources_tables.append(t)
                else:   # 没有多表关联
                    try:
                        tables = table_str.split(' ')
                        # 切割成功代表它是有别名的,取第一个作为真实表名
                        sources_tables.append(tables[0])
                    except:
                        # 切割不成功,代表它没有别名,直接添加
                        sources_tables.append(table_str)

            if ' GROUP BY ' in i and ' WHERE ' not in i:
                # 3.1.2、获取FROM到WHERE之间的部分,有逗号代表是oracle多表关联
                table_str = i[i.index(' FROM ') + 5: i.index(' GROUP BY ') + 1].strip(' ').replace('  ', ' ').\
                    replace('()', '').replace('( )', '')
                if ',' in table_str:
                    table_list = table_str.split(',')
                    for t in table_list:
                        t = t.strip(' ')
                        try:
                            tables = t.split(' ')
                            # 切割成功代表它是有别名的,取第一个作为真实表名
                            sources_tables.append(tables[0])
                        except:
                            # 切割不成功,代表它没有别名,直接添加
                            sources_tables.append(t)
                else:   # 没有多表关联
                    try:
                        tables = table_str.split(' ')
                        # 切割成功代表它是有别名的,取第一个作为真实表名
                        sources_tables.append(tables[0])
                    except:
                        # 切割不成功,代表它没有别名,直接添加
                        sources_tables.append(table_str)
            else:   # 代表直接就是一句单查询语句,没有WHERE 和 Group by,直接截取FROM后面部分
                table = i[i.index(' FROM ') + 6:].strip(' ')
                try:
                    tables = table.split(' ')
                    # 切割成功代表它是有别名的,取第一个作为真实表名
                    sources_tables.append(tables[0])
                except:
                    # 切割不成功,代表它没有别名,直接添加
                    sources_tables.append(table)

        # 对结果加工处理(对表名去重,去除分号,过滤掉表名长度在4 以下的, 可能是误提取的表名, 过滤掉with临时表写法的表名)
        sources_tables = [i.replace(';', '') for i in sources_tables]
        sources_tables = list(set([i for i in sources_tables if len(i)>4 and i not in with_query_dict['tmp_names']]))

        return sources_tables


if __name__ == '__main__':
    file_path = r'test_case\join_query.sql'
    query_sql = get_query_sql(file_path)
    sources_tables = get_tables_from_sql(query_sql)
    print(sources_tables)


4、上述脚本如何使用呢?
只要将这里的文件路径替换为你的sql文件路径即可
在这里插入图片描述
例如这样的sql文本:
注意,sql文本中只允许有一句select 语句,可以包含子查询,表关联,但只能有一句。
在这里插入图片描述
跑出来的结果:
在这里插入图片描述

5、测试

该文本目前已通过以下的测试用例(逐个查询语句作为一个sql文件,都能准确提取出来源表名)

.单表查询语句
--1、单表查询,没有过滤条件,没有分组条件
SELECT
A.HW_CONTRACT_NUM,
A.LAST_UPDATE_DATE,
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A;

SELECT
HW_CONTRACT_NUM,
LAST_UPDATE_DATE,
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP;
--2、单表查询,有过滤条件,没有分组条件
SELECT
A.HW_CONTRACT_NUM,
A.LAST_UPDATE_DATE
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
WHERE A.HW_CONTRACT_NUM = 'AAAAABBBBB'
--3、单表查询,没有过滤条件,有分组条件
SELECT COUNT(1)
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
GROUP BY A.HW_CONTRACT_NUM;
--4、单表查询,带子查询
SELECT SUM(AMOUNT)
FROM (SELECT
        A.HW_CONTRACT_NUM,
        A.LAST_UPDATE_DATE
        FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
        WHERE A.HW_CONTRACT_NUM = 'AAAAABBBBB') B
GROUP BY B.HW_CONTRACT_NUM;.多表关联查询
--1、Oracle特有写法
SELECT *
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A,
     DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B,
     DWAPP.DWL_EBG_CONTRACT_INFO_TMP3 C
WHERE 1=1
AND A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM(+)
AND A.HW_CONTRACT_NUM = C.HW_CONTRACT_NUM(+);
--2.1、sql标准写法
SELECT A.*, B.*
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM;
--2.2、sql标准写法2
SELECT A.*, B.*
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
INNER JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM;
--2.3、sql标准写法3
SELECT A.*, B.*
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
LEFT JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM;
--2.4、sql标准写法4
SELECT A.*, B.*
FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
RIGHT JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM;.多表关联查询嵌套子查询(上述所有情况把表名替换成子查询即可).WITH临时表写法
--1.1、WITH单临时表,单表查询,无过滤条件,无分组条件
WITH TMP AS (
    SELECT A.*, B.*
    FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
    RIGHT JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
    ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM
)
SELECT HW_CONTRACT_NUM FROM TMP;

--1.2、WITH单临时表,单表查询,有过滤条件,无分组条件
WITH TMP AS (
    SELECT A.*, B.*
    FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
    RIGHT JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
    ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM
)
SELECT HW_CONTRACT_NUM FROM TMP WHERE TO_CHAR(LAST_UPDATE_DATE)='2021/06/07';

--1.3、WITH单临时表,单表查询,无过滤条件,有分组条件
WITH TMP AS (
    SELECT A.*, B.*
    FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
    RIGHT JOIN DWAPP.DWL_EBG_CONTRACT_INFO_TMP2 B
    ON A.HW_CONTRACT_NUM = B.HW_CONTRACT_NUM
)
SELECT SUM(AMOUNT) FROM TMP  GROUP BY HW_CONTRACT_NUM;

--2、WITH多临时表, 单表查询...
WITH TMP1 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
),
TMP2 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP1 B
)
SELECT HW_CONTRACT_NUM FROM TMP1;
--3、WITH多临时表, 多表关联查询, ORACLE写法
WITH TMP1 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
),
TMP2 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP1 B
)
SELECT C.HW_CONTRACT_NUM, D.CUST_NUM
FROM TMP1 C,
    TMP2 D
WHERE C.XX = D.XX(+);  --(+)可有可无

--4、WITH多临时表, 多表关联查询, 标准sql写法
WITH TMP1 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP A
),
TMP2 AS (
    SELECT * FROM DWAPP.DWL_EBG_CONTRACT_INFO_TMP1 B
)
SELECT C.HW_CONTRACT_NUM, D.CUST_NUM
FROM TMP1 C
JOIN TMP2 D  --JOIN 可能替换为LEFT JOIN , INNER JOIN , RIGHT JOIN
ON C.XX = D.XX(+);


6、缺陷

说一下这个脚本的缺陷:
1、如果表别名的长度在4个字符以上(不含4),将有可能被误识别为真实的来源表名;
2、如果来源表名的长度小于或等于4个字符,会被当做表别名过滤掉;

最后,感谢一下曾文佑同学,百忙之中帮忙测试并发现了其中的bug。

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用 JSqlParser 可以很方便地从 SQL 语句提取表名和字段名。以下是一个简单的 Java 代码示例,可以从 SQL 语句提取表名和所有字段名: ```java import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SelectItem; import net.sf.jsqlparser.statement.select.SelectVisitorAdapter; public class TableAndColumnExtractor { public static void main(String[] args) throws JSQLParserException { String sql = "SELECT a.id, a.name, b.age FROM table1 a, table2 b WHERE a.id = b.id"; Statement statement = CCJSqlParserUtil.parse(sql); Select select = (Select) statement; SelectBody selectBody = select.getSelectBody(); selectBody.accept(new SelectVisitorAdapter() { @Override public void visit(PlainSelect plainSelect) { List<SelectItem> selectItems = plainSelect.getSelectItems(); for (SelectItem selectItem : selectItems) { selectItem.getExpression().accept(new SelectVisitorAdapter() { @Override public void visit(Column column) { System.out.println("Column name: " + column.getColumnName()); } }); } Table table = (Table) plainSelect.getFromItem(); System.out.println("Table name: " + table.getName()); } }); } } ``` 在上面的代码,我们首先将 SQL 语句解析为一个 Statement 对象,然后获取其的 Select 对象和 SelectBody 对象。接着,我们使用 SelectVisitorAdapter 类访问 SelectBody 对象,从提取表名和字段名。最后,我们打印出了所有的字段名和表名。 需要注意的是,JSqlParser 还支持其他类型的 SQL 语句,例如 INSERT、UPDATE 和 DELETE,您可以根据需要进行调整。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值