根据SQL提炼表信息

作者: admin 分类: 公众号存档            2 次浏览 发布时间: 2019-04-23 23:09

公众号:杨建荣的学习笔记 · 作者:杨建荣 · 发布:2019-04-23 23:09:18 · 原文链接

这是学习笔记的第 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等语句,都可以灵活的调整字典里的配置来实现。

相关链接:

admin

杨建荣,《Oracle DBA工作笔记》《MySQL DBA工作笔记》作者,dbaplus社群发起人之一,腾讯云TVP,现任竞技世界系统部经理,拥有十多年数据库开发和运维经验,目前专注于开源技术、运维自动化和性能调优

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注