使用pyparsing进行SQL解析

标签 sql parsing pyparsing

最近几周我正在学习 PyParsing。我打算用它从 SQL 语句中获取表名。
我看过http://pyparsing.wikispaces.com/file/view/simpleSQL.py .但我打算保持语法简单,因为我不想解析 select 语句的每个部分,而是只查找表名。此外,为任何商用现代数据库(如 Teradata)定义完整的语法也非常复杂。

#!/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']

当 SELECT 有 where 子句时,我遇到了问题:
nested_table = lparen.suppress() + select_kw.suppress() + SkipTo(from_kw).suppress() + \   
               from_kw.suppress() + table + rparen.suppress()

当 WHERE 子句存在时,相同的语句可能如下所示:
SELECT ... FROM a,d , (SELECT * FROM z WHERE (c1 = 1) and (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 中获取表名的最佳方法。任何帮助都非常感谢。

谢谢
阿比 git

最佳答案

考虑到您还试图解析嵌套的 SELECT,我认为您无法避免编写相当完整的 SQL 解析器。幸运的是,Pyparsing wiki 示例页面上有一个更完整的示例,select_parser.py .我希望这能让你走得更远。

关于使用pyparsing进行SQL解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16909380/

相关文章:

mysql - SQL Inner Join 返回奇怪的结果

php - MYSQL 查询查找已审查的服务数量

python - pyparsing - 同一条语句的第二次执行抛出异常

python - 如何将字符串逐行提供给pyparsing?

sql - 在 Redshift 上创建 Apriori 项集

JavaScript:本地运行 JS-XSL 演示时为 "Uncaught SecurityError"

java - 使用解析服务器在 recyclerview 上没有显示图像

javascript - Javascript 中的用户代理解析

python - Pyparsing:在parseaction中访问外部ParseResults

mysql - MySQL 上共同好友订购的好友推荐