2017-05-26 430 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>.*?)\)\;$ 

在命名捕获组的结果然后可以在逗号分割。