2016-02-25 120 views
3

因此,讓我們假設有這樣的簡單查詢:如何從sql查詢中提取表名和列名?

Select a.col1, b.col2 from tb1 as a inner join tb2 as b on tb1.col7 = tb2.col8; 

結果應該看起來是這樣的:

tb1 col1 
tb1 col7 
tb2 col2 
tb2 col8 

我試着使用一些Python庫來解決這個問題:

1 )即使僅使用sqlparse提取表格也可能是一個巨大的問題。例如this官方書籍根本無法正常工作。

2)使用正則表達式似乎很難實現。

3)但後來我發現this,這可能會有所幫助。但問題是我無法連接到任何數據庫並執行該查詢。

任何想法?

+0

谷歌搜索 「蟒蛇SQL語法分析程序」

import re def tables_in_query(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. if we see a FROM or JOIN, we set the get_next # flag, and grab the next one (unless it's SELECT). table = set() get_next = False for tok in tokens: if get_next: if tok.lower() not in ["", "select"]: table.add(tok) get_next = False get_next = tok.lower() in ["from", "join"] dictTables = dict() for table in tables: fields = [] for token in tokens: if token.startswith(table): if token != table: fields.append(token) if len(list(set(fields))) >= 1: dictTables[table] = list(set(fields)) return dictTables 

代碼變成了這太問題:http://stackoverflow.com/questions/1394998/parsing-sql-with-python – PaulMcG

回答

2

真的,這不是一件容易的事。您可以使用詞法分析器(本例中的ply)並定義幾個規則以從字符串中獲取多個令牌。以下代碼爲SQL字符串的不同部分定義了這些規則,並將它們放回到一起,因爲輸入字符串中可能有別名。因此,您會得到一個詞典(result),其中不同的表名稱爲鍵。

import ply.lex as lex, re 

tokens = (
    "TABLE", 
    "JOIN", 
    "COLUMN", 
    "TRASH" 
) 

tables = {"tables": {}, "alias": {}} 
columns = [] 

t_TRASH = r"Select|on|=|;|\s+|,|\t|\r" 

def t_TABLE(t): 
    r"from\s(\w+)\sas\s(\w+)" 

    regex = re.compile(t_TABLE.__doc__) 
    m = regex.search(t.value) 
    if m is not None: 
     tbl = m.group(1) 
     alias = m.group(2) 
     tables["tables"][tbl] = "" 
     tables["alias"][alias] = tbl 

    return t 

def t_JOIN(t): 
    r"inner\s+join\s+(\w+)\s+as\s+(\w+)" 

    regex = re.compile(t_JOIN.__doc__) 
    m = regex.search(t.value) 
    if m is not None: 
     tbl = m.group(1) 
     alias = m.group(2) 
     tables["tables"][tbl] = "" 
     tables["alias"][alias] = tbl 
    return t 

def t_COLUMN(t): 
    r"(\w+\.\w+)" 

    regex = re.compile(t_COLUMN.__doc__) 
    m = regex.search(t.value) 
    if m is not None: 
     t.value = m.group(1) 
     columns.append(t.value) 
    return t 

def t_error(t): 
    raise TypeError("Unknown text '%s'" % (t.value,)) 
    t.lexer.skip(len(t.value)) 

# here is where the magic starts 
def mylex(inp): 
    lexer = lex.lex() 
    lexer.input(inp) 

    for token in lexer: 
     pass 

    result = {} 
    for col in columns: 
     tbl, c = col.split('.') 
     if tbl in tables["alias"].keys(): 
      key = tables["alias"][tbl] 
     else: 
      key = tbl 

     if key in result: 
      result[key].append(c) 
     else: 
      result[key] = list() 
      result[key].append(c) 

    print result 
    # {'tb1': ['col1', 'col7'], 'tb2': ['col2', 'col8']}  

string = "Select a.col1, b.col2 from tb1 as a inner join tb2 as b on tb1.col7 = tb2.col8;" 
mylex(string) 
+0

你對,這個任務真的很難。但它似乎很受歡迎,我相信它已經解決了。 – Rocketq

+0

我得到這個錯誤:TypeError:是一個內置模塊 (PS。我不是Python的專家) –