根据SQL提炼表信息

这是学习笔记的第 1958 篇文章


在MySQL慢日志通过pt工具解析后有一个小问题,那就是我们很难得到一个完整的table列表,即假设一条SQL语句很长,那么我们其实想知道这条SQL相关的表是哪几个,如果能够很清晰的提炼出来,那么我们要做后续的分析就会锦上添花。

显然这个工作是有一些复杂度的,大体想了下,有以下的一些难点:

  1. select语句如果是多个表关联,那么这个提取工作就会增加解析的复杂度

  2. insert语句如果是insert xxx select xxx from的形式,那么得到的相关表应该是两个以上

  3. 如果是replace,load等语句,解析规则需要自己定制,目前没有通用的方法

  4. 如果语句含有注释,换行等符号,需要甄别和过滤

所以这一块算是有一些实现难度的功能,我们可以简化下思路,可以把SQL语句先做下格式化,把回车,换行,注释等都统一处理下。

这样在一个基本规范的前提下,我们把SQL语句切分成多份,然后按照语句的规则来做匹配,比如对于select语句,我们的提炼逻辑是找到from和join关键字的位置,进行table的信息提取。

关于语句的格式化部分,可以使用re来实现,主要是做正则表达式的相关逻辑检查。

然后指定一个字典结构,根据自定义的SQL匹配规则进行信息补充,这样对于insert语句,我们可以灵活的匹配insert select from的场景。

如果有些表名重复,为了去重排序,我们可以使用set的数据结构。

整体的代码实现如下:

import re

def extract_table_name_from_sql(sql_str):


    # remove the /* */ comments

    q = re.sub(r"/\*[^*]*\*+(?:[^*/][^*]*\*+)*/", "", sql_str)


    # remove whole line -- and # comments

    lines = [line for line in q.splitlines() if not re.match("^\s*(--|#)", line)]


    # remove trailing -- and # comments

    q = " ".join([re.split("--|#", line)[0] for line in lines])


    # split on blanks, parens and semicolons

    tokens = re.split(r"[\s)(;]+", q)


    # scan the tokens with user-defined logic to get table info list

    result = set()

    if tokens.__len__()>0:

        sql_type = tokens[0]

    else:

        sql_type = "dummy"

    #print sql_type

    sql_type_map = {}

    sql_type_map["select"] =["from", "join"]

    sql_type_map["update"] =["update","from"]

    sql_type_map["insert"] =["into","from","join"]

    sql_type_map["delete"] =["from"]

    sql_type_map["load"] =["table"]

    sql_type_map["replace"] =["into"]

    sql_type_map["alter"] =["table"]


    for idx, token in enumerate(tokens):

        if token.lower() in sql_type_map[sql_type.lower()]:

            result.add(tokens[idx+1])

    return ",".join(str(i) for i in list(result))


打个比方,我们使用如下的测试SQL,需要解析table列表:

sql8="insert into test select xxxx from aa join b where aaaaa"

print(extract_table_name_from_sql(sql8))

得到的结果如下:

test,aa,b

对于select,load,update等语句,都可以灵活的调整字典里的配置来实现。


相关链接:


640?


猜你喜欢

转载自blog.csdn.net/weixin_36250635/article/details/89485270