2017-05-26 429 views
2

我正在使用django網站,並且我正在嘗試使用從舊數據庫轉儲的數據爲django創建YAML裝置。我知道,我知道..但是我找不到任何能夠幫助我快速完成這個任務的東西,所以我必須「自己動手」 - 除非有更好的解決方案建議)。Python正則表達式匹配SQL INSERT語句

「滾動我自己的」解決方案的一部分是解析SQL語句 - 這些是自動生成的,因此語句的格式不會改變。

這裏有兩個樣品INSERT聲明:

INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)'); 
INSERT INTO ref_currency_group (name) VALUES('Major'); 

我想將SQL語句神交成以下模式:

INSERT INTO <table_name> VALUES (one_or_more_alphanums_separated_by_comma); 

然後我需要符合以下值:

  • table_name
  • one_or_more_alphanums_separated_by_comma

這是我的正則表達式模式。它是匹配的,但分組不是我想要的。

pattern_string = r"INSERT INTO ([a-zA-Z\_]+)\s\(((([a-zA-Z\_]+)(\,)*)+)\)\s+VALUES\(([0-9]*)|([a-zA-Z\(\)']+)(\,)*\;" 

如何修改(並簡化)上面的模式,以便它只匹配我感興趣的令牌?

+0

這不是對問題的直接回答 - 而且您可能已經嘗試過並發現它不足 - 但會將該SQL加載到數據庫中,[在數據庫上運行inspectdb以獲取初始Django模型文件]( https://docs.djangoproject.com/en/1.11/howto/legacy-databases/),然後使用'dumpdata'把它變成燈具幫助? – bouteillebleu

+0

考慮寫一個解析器,而不是濫用正則表達式捕獲組。我在這裏回答了一個類似的問題:https://stackoverflow.com/questions/42435114/in-python-how-to-parse-a-string-representing-a-set-of-keyword-arguments-such-th/42437175 #42437175。如果這有幫助,那很好。如果沒有,讓我知道,我會看看我是否可以爲你想要製作的語言打出語法。 – ymbirtt

回答

0

如果語句的格式是固定的,那麼使用正則表達式沒什麼意義。只需使用簡單的字符串解析:

parts = statement.split(' ', 4) 

print(parts[2]) 
print(parts[3][1:-1].split(',')) 
print(parts[4][7:-2].split(',')) 

輸出示例:

ref_geographic_region 
['continent_id', 'name'] 
['8', "'Europe (Western)'"] 
+0

我喜歡這個答案。這是最簡單的方法 - 我不知道爲什麼它不會出現在我的... –

1

停止試圖解析SQL用正則表達式。這與用正則表達式解析HTML差不多,因爲SQL是一種上下文無關語言,正則表達式不適合處理。這可以更容易地與合適的解析模塊來完成像PyParsing

from pyparsing import Regex, QuotedString, delimitedList 

# Object names and numbers match these regular expression 
object_name = Regex('[a-zA-Z_]+') 
number = Regex('-?[0-9]+') 
# A string is just something with quotes around it - PyParsing has a built in 
string = QuotedString("'") | QuotedString('"') 

# A term is a number or a string 
term = number | string 

# The values we want to capture are either delimited lists of expressions we know about... 
column_list = (delimitedList(object_name)).setResultsName('columns') 
term_list = (delimitedList(term)).setResultsName('terms') 

# Or just an expression we know about by itself 
table_name = object_name.setResultsName('table') 

# And an SQL statement is just all of these pieces joined together with some string between them 
sql_stmt = "INSERT INTO " + table_name + "(" + column_list + ") VALUES(" + term_list + ");" 


if __name__ == '__main__': 
    res = sql_stmt.parseString("""INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)');""") 
    print res.table   # ref_geographic_region 
    print list(res.columns) # ['continent_id', 'name'] 
    print list(res.terms) # ['8', 'Europe (Western)'] 

這是一個快速半小時稻草人 - 我想通過它docs建議閱讀和獲取它是如何工作的正確理解。特別是,PyParsing在空白處有一些奇怪的行爲,在您正確觸發之前值得理解。

+0

+1推薦PyParsing(看起來像一個有趣的軟件包) - 我會用它在未來的項目,但我覺得它是過度的對於這個特定的問題。 –

0

對我來說,你只是想從插入語句中刪除字段名。

在這種情況下,您可以匹配語句的第一部分並從中刪除字段。

例如:

import re 

lines = [ 
    "INSERT INTO ref_geographic_region (continent_id,name) VALUES(8,'Europe (Western)');", 
    "INSERT INTO ref_currency_group (name) VALUES('Major');" 
] 

insertStatementPattern = re.compile(r'^(insert into\s+\S+)\s*\([^()]+\)', re.IGNORECASE) 

for i, line in enumerate(lines): 
    line = re.sub(insertStatementPattern, r'\1', line) 
    print (i, line) 

更完整的正則表達式來捕捉字段名和值:

(?i)^insert into\s*(?P<tablename>\w+)\s*\((?P<fieldnames>[^()]+)\)\s*values\s*\((?P<fieldvalues>.*?)\)\;$ 

在命名捕獲組的結果然後可以在逗號分割。