【问题标题】:Stuck parsing SQL Select statement at the join clause (BNF grammar included)在连接子句处解析 SQL Select 语句(包括 BNF 语法)
【发布时间】:2019-10-17 19:20:33
【问题描述】:

我很难理解如何使用此 SQL 语法从解析器解析下面提供的 SQL 语句。我被困在 WHERE 令牌之后发现的“表参考”和“加入”构造。

BNF:http://www.contrib.andrew.cmu.edu/~shadow/sql/sql2bnf.aug92.txt

<table reference> ::=
         <table name> [ <correlation specification> ]
     | <derived table> <correlation specification>
     | <joined table>

 <joined table>    ::=
         <cross join>
     | <qualified join>
     | <left paren> <joined table> <right paren>

<cross join>    ::=
         <table reference> CROSS JOIN <table reference>

<qualified join>    ::=
         <table reference> [ NATURAL ] [ <join type> ] JOIN <table reference> [ <join specification> ]

<join type>    ::=
         INNER
     | <outer join type> [ OUTER ]
     | UNION

<outer join type>    ::=   LEFT | RIGHT | FULL

<join specification>    ::=   <join condition> | <named columns join>

<join condition>    ::=   ON <search condition>

<named columns join>    ::=   USING <left paren> <join column list> <right paren>

SQL:

SELECT p.Name, v.Name
FROM Production.Product p
JOIN Purchasing.ProductVendor pv
ON p.ProductID = pv.ProductID
JOIN Purchasing.Vendor v
ON pv.BusinessEntityID = v.BusinessEntityID
WHERE ProductSubcategoryID = 15
ORDER BY v.Name; 

跳转到 FROM 子句。这里有一个 TableName,后面跟着两个 JOIN。

如果您查看“表参考”,您会看到其中包含“表名称”。这个我能理解。

<table reference>    ::=
         **<table name> [ <correlation specification> ]**
     | <derived table> <correlation specification>
     | <joined table>

但是要获得连接,解析器必须到达它不能到达的“连接表”,因为它已经准备好读取“表名”。

要达到连接,解析器必须达到“合格连接”,但它不能,因为 BNF 中没有重复。如果它以某种方式到达“连接表”元素,那么如果它会非常失望,因为下一次读取将再次是“表引用”,然后它会再次到达“合格连接”,然后......然后你会得到一个堆栈溢出。

  <joined table>    ::=
         <cross join>
     | <**qualified join>**
     | <left paren> <joined table> <right paren>

**<qualified join>**    ::=
         <table reference> [ NATURAL ] [ <join type> ] JOIN <table reference> [ <join specification> ]

我在这里没有得到什么?我敢肯定它有一个技巧,但我只是没有看到它。

我希望你们中的一些人可以向我解释这对我来说不可能的语法是怎么回事。

如何构造一个比方说递归下降解析器来解决这个语法?

解析器需要遵循哪些步骤和/或规则?

【问题讨论】:

  • 我对你在做什么不是很熟悉,但是......我正在阅读的是&lt;table name&gt; 表示其中只有一个一个表的查询,而&lt;joined table&gt; 表示其中包含连接表的查询。但是你如何应用逻辑来事先识别这一点我不确定。
  • 没错。您如何告诉递归体面的 paser 选择正确的路径?我的另一个问题是 BNF 如何支持嵌套连接?正确的解释可能很简单。
  • 你为什么认为语法必须可以用递归下降解析器解析?
  • 我不知道是否可以使用递归的体面解析器解析此语法?我自己正在尝试用这样的解析器来实现它。
  • 我不太明白:这是您试图理解的经过验证的解析器,还是您自己构建解析器?换句话说,解决方案是更好地理解 BNF,还是解决方案是对 BNF 的更改,还是别的什么? @rici 提出了一个很好的观点——我认为你不能用串行方法解析 SQL,你可能需要从高层次识别组件并深入研究这些部分

标签: sql parsing bnf


【解决方案1】:

该语法不是 LL(1),这是构建递归下降解析器所需要的。我怀疑是否有适用于 SQL 的 LL(1) 语法,尤其是如果有一种可以生成正确的解析树的语法。幸运的是,这并不重要,因为有更强大的解析技术。

您很可能可以使用该语法构建一个 LALR(1) 解析器,使用像 bison/yacc 这样的工具。或者查看sqlite source code,它包括一个 LALR(1) 语法和一个名为“lemon”的 LALR(1) 解析器生成器。

【讨论】:

  • 谢谢。这正是我需要的信息。我认为我需要将我的知识扩展到 RDP/LL(1) 之外。我会阅读解析器并查看您的链接。
  • 你知道将 LALR(1) BNF 转换为 LL(1) BNF 的技巧吗?
  • @brian 一般来说,转换是不可能的,因为 LR 文法集是 LL 文法的严格超集。可以尝试一些机械转换,但它们在玩具语法上的效果要好于 SQL 等现实问题。即使他们产生了无法保证的 LL 语法,解析树也不会被保留,因此您最终会遇到不同的问题。恕我直言,最好的技巧是找到一个适用于您的编码语言的解析器生成器,并学习如何使用它。
【解决方案2】:

自从提出这个问题以来已经有一段时间了,但由于 ANTLR 的性能/内存问题,我正在考虑将我的 ANTLR4 SQL 解析器重写为 RDP。

所以,我(精神上)解决 JOIN 问题的方式是我将它们视为运算符,因为它们就是这样(至少这是我的印象)。

基本表达式语法既适合常用表达式也适合 JOIN:

ex
    : '(' ex ')'
    | ex OPERATOR ex
    | TERMINAL
    ;

在 JOIN 情况下,您的 OPERATOR 变为 JOIN,TERMINAL 变为表引用。

现在,这种左递归文法不是 LL 文法,但它可以转化为 ANTLR4 自动完成的。

【讨论】:

    猜你喜欢
    • 2019-08-24
    • 2016-06-21
    • 1970-01-01
    • 2011-06-10
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    相关资源
    最近更新 更多