基於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)
以上代碼,簡單的實現了目前的需求,后續將這部分做成工具,可以方便后續的使用。結合其他文件處理工具,可以更好的處理各種情況
參考
參考了部分正則表達式的語法等數據