2013-06-04 71 views
3

我在过去的几周里学习了PyParsing。我打算用它从SQL语句中获取表名。 我查看了http://pyparsing.wikispaces.com/file/view/simpleSQL.py。但我打算保持简单的语法,因为我不想让每个部分的语句都被解析,而只是寻找表名。此外,还需要为诸如Teradata之类的任何商用现代数据库定义完整的语法。使用pyparsing进行SQL解析

#!/usr/bin/env python 

from pyparsing import * 
import sys 

semicolon = Combine(Literal(';') + lineEnd) 
comma = Literal(',') 
lparen = Literal('(') 
rparen = Literal(')') 

# Keyword definition 
update_kw, volatile_kw, create_kw, table_kw, as_kw, from_kw, \ 
where_kw, join_kw, left_kw, right_kw, cross_kw, outer_kw, \ 
on_kw , insert_kw , into_kw= \ 
    map(lambda x: Keyword(x, caseless=True), \ 
     ['UPDATE', 'VOLATILE', 'CREATE', 'TABLE', 'AS', 'FROM', 
     'WHERE', 'JOIN' , 'LEFT', 'RIGHT' , \ 
     'CROSS', 'OUTER', 'ON', 'INSERT', 'INTO']) 

# Teradata SQL allows SELECT and well as SEL keyword 
select_kw = Keyword('SELECT', caseless=True) | Keyword('SEL' , caseless=True) 

# list of reserved keywords 
reserved_words = (update_kw | volatile_kw | create_kw | table_kw | as_kw | 
        select_kw | from_kw | where_kw | join_kw | 
        left_kw | right_kw | cross_kw | on_kw | insert_kw | 
        into_kw) 

# Identifier can be used as table or column names. They can't be reserved words 
ident = ~reserved_words + Word(alphas, alphanums + '_') 

# Recursive definition for table 
table = Forward() 
# simple table name can be identifer or qualified identifier e.g. schema.table 
simple_table = Combine(Optional(ident + Literal('.')) + ident) 
# table name can also a complete select statement used as table 
nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \ 
       from_kw.suppress() + table + rparen.suppress() 
# table can be simple table or nested table 
table << (nested_table | simple_table) 
# comma delimited list of tables 
table_list = delimitedList(table) 
# Building from clause only because table name(s) will always appears after that 
from_clause = from_kw.suppress() + table_list 


txt = """ 
SELECT p, (SELECT * FROM foo),e FROM a, d, (SELECT * FROM z), b 
""" 
for token, start, end in from_clause.scanString(txt): 
    print token 

这里值得一提的东西。我使用“SkipTo(from_kw)”跳过SQL语句中的列列表。这主要是为了避免为列列表定义语法,可以用逗号分隔列表标识符,许多函数名称,DW分析函数以及不包含的语法。有了这个语法,我可以解析上面的语句以及SELECT列表或列表中的任何级别的嵌套。

['foo'] 
['a', 'd', 'z', 'b'] 

我面临的问题时,选择具有where子句:

nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \ 
       from_kw.suppress() + table + rparen.suppress() 

当WHERE子句是有那么同样的语句可能看起来像: SELECT ... FROM A,d,(SELECT *从z其中(C1 = 1)和(C2 = 3)),p 我想改变 “nested_table” 定义的:

nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \ 
       from_kw.suppress() + table + Optional(where_kw + SkipTo(rparen)) + rparen 

但是这是行不通的,因为它的权利消减匹配“c = 1”后的合成。我想知道的是如何跳到右括号匹配左括号右“SELECT * FROM z ...”之前我不知道如何使用PyParsing

另外在不同的笔记上我寻求一些建议,从复杂的嵌套SQL获取表名的最佳方式。任何帮助真的很感激。

感谢 作者Abhijit

+0

任何人的帮助? –

回答

5

考虑,你也试图分析出嵌套的SELECT的,我不认为你将能够避免写一个相当完整的SQL语法分析程序。幸运的是,在Pyparsing wiki Examples页面上有一个更完整的例子,select_parser.py。我希望能让你更进一步。

+0

保罗,真的很感谢你的指导。不用说我成了Pyparsing的粉丝! –