使用Python解析SQL

62

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

我正在研究使用ANTLR来生成表示SQL的关系代数表达式的AST。然后通过评估/遍历树来返回数据。

我以前从未实现过解析器,因此我希望能够获得如何最佳实现SQL解析器和求值器的建议。

  • 以上述方法是否正确?
  • 是否应该查看其他工具/库?例如PLYPyparsing
  • 指向可帮助我的文章,书籍或源代码的指针将不胜感激。

更新:

我使用pyparsing实现了一个简单的SQL解析器。结合实现与我的数据仓库进行关系操作的Python代码,这相当简单。

正如我在评论中所说,这个项目的目的是使数据对报告引擎可用。为此,我可能需要实现ODBC驱动程序。这可能是很多工作。


3
为什么要对对象施加SQL限制?有什么好处?OQL有什么问题吗?http://en.wikipedia.org/wiki/Object_Query_Language - S.Lott
10
目标:提供一个查询接口,可以被大量报表工具使用。我计划在客户端实现一个ODBC驱动程序,以便业务用户可以使用Crystal Reports、Excel等工具从数据存储区检索数据。虽然OQL可能是一种不错的查询语言(我从未使用过),但它并不像SQL那样广泛使用。 - codeape
3
+1:面向对象数据库最大的问题之一就是缺乏报告引擎 :( - van
6个回答

46

我已经广泛研究了这个问题。Python-sqlparse是一个非验证解析器,不是您需要的东西。Antlr中的示例需要大量工作才能转换为Python中的良好抽象语法树。SQL标准语法在这里,但将它们转换成自己的语法将是一项全职工作,并且很可能只需要其中的一部分,例如没有联接。您可以尝试查看gadfly(一个Python SQL数据库),但我避免使用它,因为他们使用了自己的解析工具。

对于我的情况,我仅实际上需要where子句。我尝试过用pyparsing编写的booleneo(布尔表达式解析器),但最终选择从头开始使用pyparsing。Mark Rushakoff的Reddit帖子中的第一个链接给出了使用它的SQL示例。全文搜索引擎Whoosh也使用它,但我没有查看源代码来了解它的具体情况。

pyparsing非常易于使用,您可以非常容易地进行自定义,使其不完全与SQL相同(大多数语法您都不需要)。我不喜欢ply,因为它使用某些魔术命名约定。

简而言之,请尝试使用pyparsing,它很可能足够强大以执行所需的操作,并且与Python易于集成(具有易于回调和错误处理的功能),这将使体验非常轻松。


1
感谢分享你的经验。从最初的非常有限的对python-sqlparse的测试中,看起来我可能能够使用它。我将尝试使用parse函数在python-sqlparse中返回的值进行操作。但无论如何,我还会研究一下pyparsing。 - codeape
1
Pyparsing是一个很好的工具,有很多解析SQL的示例。 - Gregg Lind
2
这篇海报在pyparsing wiki上(http://pyparsing.wikispaces.com/message/view/home/14105203)报告完成了一个SQL SELECT解析器 - 也许你可以联系他/她寻求帮助、建议,甚至是代码。 - PaulMcG
TFTT。我已联系发帖人。 - codeape
3
我使用了pyparsing来实现它,这个工具在这方面表现得非常出色。 - codeape

13

谢谢您的建议。Python-sqlparse看起来很有趣,我会试一试。 - codeape
1
这篇文章发布已经有一段时间了 - 现在有一个基于sqlparse库构建的https://github.com/macbre/sql-metadata。 sql-metadata不会给你AST,但会从查询中提取已解析的列和表名。 - Dylan Hogg
sql-metadata 真是太棒了,我一直在寻找一个 DDL 解析器,尽管它不能处理 DDLs,但它是如何使用标记器的绝佳示例。 - Erik Aronesty

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']

1
Python SQL解析器不支持Python 3。 - Mindaugas Jaraminas

2

SQLGlot 是一个无依赖的SQL解析器、编译器、优化器和引擎。语法错误会被突出显示。然而,需要注意的是,SQL验证不是SQLGlot的目标,因此可能会忽略一些语法错误。( reddit) (benchmarks)

sqltree 是SQL的实验性解析器,为SQL查询提供语法树。

sample-sql-translator 是一个手写的递归下降解析器,用于解析SQL。

sqloxidesqlparser-rs的Rust绑定封装成一个Python包。


1

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接