这是学习笔记的第 1958 篇文章
在MySQL慢日志通过pt工具解析后有一个小问题,那就是我们很难得到一个完整的table列表,即假设一条SQL语句很长,那么我们其实想知道这条SQL相关的表是哪几个,如果能够很清晰的提炼出来,那么我们要做后续的分析就会锦上添花。
显然这个工作是有一些复杂度的,大体想了下,有以下的一些难点:
所以这一块算是有一些实现难度的功能,我们可以简化下思路,可以把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等语句,都可以灵活的调整字典里的配置来实现。