用Python解析SQL

62 投票
6 回答
75681 浏览
提问于 2025-04-15 14:09

我想在一个非关系型数据存储上创建一个SQL接口。虽然这个数据存储不是关系型的,但以关系型的方式访问数据是有意义的。

我正在考虑使用ANTLR来生成一个抽象语法树(AST),这个树可以表示SQL作为关系代数表达式。然后通过评估或遍历这棵树来返回数据。

我之前从来没有实现过解析器,所以我想请教一下,如何最好地实现一个SQL解析器和评估器。

  • 上面描述的方法听起来对吗?
  • 还有其他我应该了解的工具或库吗?比如PLYPyparsing
  • 如果有相关文章、书籍或源代码推荐给我,那就太好了。

更新:

我用pyparsing实现了一个简单的SQL解析器。结合Python代码来对我的数据存储执行关系操作,这个过程相对简单。

正如我在评论中提到的,这个练习的目的是为了让数据可以被报表引擎使用。为此,我可能需要实现一个ODBC驱动程序。这可能会需要很多工作。

6 个回答

5

TwoLaid的Python SQL解析器对我来说非常好用。它是用C语言写的,需要编译。这个解析器很强大,可以把每个语句的各个部分分开。

https://github.com/TwoLaid/python-sqlparser

我用它来提取查询中的列名,以便在报告的标题中使用。下面是一个例子。

import sqlparser

def get_query_columns(sql):
   '''Return a list of column headers from given sqls select clause'''

   columns = []

   parser = sqlparser.Parser()

   # Parser does not like new lines
   sql2 = sql.replace('\n', ' ')

   # Check for syntax errors
   if parser.check_syntax(sql2) != 0:
      raise Exception('get_query_columns: SQL invalid.')

   stmt = parser.get_statement(0)
   root = stmt.get_root()
   qcolumns = root.__dict__['resultColumnList']
   for qcolumn in qcolumns.list:
      if qcolumn.aliasClause:
         alias = qcolumn.aliasClause.get_text()
         columns.append(alias)
      else:
         name = qcolumn.get_text()
         name = name.split('.')[-1] # remove table alias
         columns.append(name)

   return columns

sql = '''
SELECT 
   a.a,
   replace(coalesce(a.b, 'x'), 'x', 'y') as jim,
   a.bla as sally  -- some comment
FROM
   table_a as a
WHERE
   c > 20
'''

print get_query_columns(sql)

# output: ['a', 'jim', 'sally']
13

这篇Reddit帖子提到了一些现成的解决方案,其中包括python-sqlparse,还有其他几个链接。

46

我对这个问题研究了很久。Python-sqlparse 是一个不进行验证的解析器,这其实不太符合你的需求。antlr 中的例子需要很多工作才能转换成 Python 中好用的抽象语法树(AST)。SQL 的标准语法可以在这里找到,但自己转换这些语法几乎就像是一份全职工作,而且你可能只需要其中的一小部分,比如不需要连接查询。你也可以看看gadfly(一个 Python SQL 数据库),但我没有选择它,因为它使用了自己的解析工具。

对我来说,我基本上只需要一个 where 子句。我尝试过booleneo(一个布尔表达式解析器),它是用 pyparsing 写的,但最后还是决定从头开始使用 pyparsing。Mark Rushakoff 在 Reddit 帖子中的第一个链接给出了一个使用它的 SQL 示例。Whoosh 是一个全文搜索引擎,也使用了它,不过我还没有查看源代码来了解具体是怎么做的。

Pyparsing 非常容易使用,你可以很简单地自定义它,使其不完全和 SQL 一样(大部分语法你可能用不到)。我不喜欢 ply,因为它使用了一些命名约定的魔法。

总之,试试 pyparsing 吧,它很可能足够强大来满足你的需求,而且与 Python 的简单集成(有简单的回调和错误处理)会让整个过程变得相当轻松。

撰写回答