基于Python实现解析SQL代码中的表
1.问题:
有一批SQL代码,需要提取其中用到的表。
2.实现思路:
01.通过正则匹配的方式,将sql分为三类 create from|join insert的这几种情况
02. 使用脚本语言Python开发,快捷
3.注意事项
01.前提假设:
SQL都是规范的可运行的。 schema.table_name 的形式或者 table_name的形式,
如果schema 和 table_name之间有多个空格的情况,这种要特殊处理一下
test_a. my_table_nm 这种情况会把只提取出 test_a. 的形式,针对这种情况,可以采用以下方式
处理例如: line = sub(r"test_a. ", r'test_a.', line)
02. 注释的代码不需要
代码实现
代码中内容,只是日常处理数据使用,没有考虑正式生产环境,比如没有日志记录等部件。如果要到生产环境,要做的工作还不少。
在日常中使用,如果要用多次的话,还是写成工具的形式
#!/usr/bin/env python
# -*-coding:utf-8-*-
# @file extract_sql_table.py
from re import match, sub, compile
import os
import pandas as pd
def extract_pure_field(sql_file_name, res_file):
"""去除多余空格,空行, 注释 配置 等"""
# 后缀换成 txt
with open(sql_file_name, mode='r', encoding='utf8') as fileObj, \
open(res_file, mode='w', encoding='utf8') as f2:
for line, data in enumerate(fileObj):
# .strip() 去除字符串首尾的空格
line_text = data.strip().lower()
# 有 insert 的行<默认insert 紧跟着表且在同一行>
if match('^--.*|^set .*', line_text) is None:
# 多个空格变为一个空格
line_text_after = sub(' +', ' ', line_text)
print("行数 ", line, sep=',')
f2.writelines(line_text_after+"\r\n" + " ")
def extract_table_name(sql_file_name, extract_flag='3'):
"""提取from 或者 join后面的 Table
extract_flag='1' overwrite|into 后的表名
extract_flag='2' create 后的表名
extract_flag='3' from|join 的表名
"""
with open(sql_file_name, mode='r', encoding='utf-8') as fileObj:
job_file = os.path.split(os.path.splitext(sql_file_name)[0])[1]
# read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中
lines = fileObj.read()
# 删除空行
line_text_after = sub(r"\n[\s| ]*\n", '', lines)
# windows的换行是\r\n,unix的是\n,mac的是\r-变成一行的字符串
# python本身对string长度无强制性限制。使用过程中主要需要考虑电脑性能和程序效率
# 将文档变为一行,解决换行引起的问题,这里还可以通过正则的方式,在这里就暂时没考虑这种实现
line = sub(' +', ' ', line_text_after.replace('\n', '').replace('\r', '')).lower()
# 部分表名不规范 text. Test_D_history 多出了个空格,这种情况处理需额外添加条件处理
# 数字、26个英文字母或者下划线 和 英文句号组成的字符串,这部分的正则表达式可以再了解了解
if extract_flag == '1':
pattern_tuple = compile('insert (?:overwrite table|into table|overwrite|into) [0-9a-zA-Z_\\.]{1,}')
pattern_string = "overwrite|into"
table_location = -1
elif extract_flag == '2':
pattern_tuple = compile('create (?:table if not exists|table) [0-9a-zA-Z_\\.]{1,}')
pattern_string = "create"
table_location = -1
else:
pattern_tuple = compile('(?:from|join) [0-9a-zA-Z_\\.]{1,}.*?')
pattern_string = 'from|join'
table_location = 1
# 表名提取
# 存储结果
table_list = []
data_tuple = pattern_tuple.findall(line)
for table in data_tuple:
table_name = table.split(" ")[table_location]
comb_job_table_data = job_file, pattern_string, table_name
print(comb_job_table_data)
table_list.append(comb_job_table_data)
return table_list
if __name__ == '__main__':
infile_name = r"C:/Users/Desktop/test.sql"
out_put_file = r"C:/Users/Desktop/select_table_nm.txt"
res_file = os.path.splitext(infile_name)[0] + '.txt'
# 处理文件注释等情况
extract_pure_field(infile_name, res_file)
# 提取表 1 是 overwrite|into表, 2是create 其余情况是 from|join
table_job_list = extract_table_name(res_file, "3")
# 将表写到数据框
result_data = pd.DataFrame(table_job_list).drop_duplicates()
# 追加的形式写入
result_data.to_csv(path_or_buf=out_put_file, mode='a', index=False, header=False)
# 移除中间文件
#os.remove(res_file)
以上代码,简单的实现了目前的需求,后续将这部分做成工具,可以方便后续的使用。结合其他文件处理工具,可以更好的处理各种情况
参考
参考了部分正则表达式的语法等数据