数据开发_Python解析sql提取表

基于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)

以上代码,简单的实现了目前的需求,后续将这部分做成工具,可以方便后续的使用。结合其他文件处理工具,可以更好的处理各种情况

参考

 参考了部分正则表达式的语法等数据