【问题标题】:Doing BNF for a simplified SQL “where” clause为简化的 SQL “where”子句执行 BNF
【发布时间】:2021-12-10 01:18:51
【问题描述】:

我有以下应用了基本 BNF 的 SQL 语句:

SELECT
    from_expression [, from_expression] ...
FROM
    from_source
WHERE
    condition


from_source:
    table_name

from_expression:
    literal              # 'abc', 1, 
    column               # Table.Field1, Table.Profit
    function             # ABS(...)
    operator invocation  # NOT field1, 2+3, Genres[0], Genres[1:2], Address.name

condition:
    ???

目前WHERE 条件与from_expression 相同,但被评估为布尔值。什么是正确的展示方式?

【问题讨论】:

  • 可能从括号、连词、析取开始,然后是支持的条件类型(在这个简化的语法中可能限制了操作数的产生)。与其他语言不同,文字不是条件的有效表达式。在语法中没有必要表明“被评估为布尔值”。
  • 这是一个浓缩的提取物,可能看起来像:cond_or: cond or cond, cond: expr = expr | expr < expr | expr is null, expr: literal | identifier 等。expr 不会直接反馈给 cond。
  • @David542 我编写了 ORM 的 LiveSQL 模块,它允许开发人员即时组装通用选择语句。作为一般规则,SQL 表达式可以分为 6 种类型表达式,具体取决于它们的结果类型(数值、字符串、时间、布尔值、二进制等)。您的“来自表达式”可以是六种类型表达式中的任何一种,而您的“条件”只能是布尔表达式(又名“谓词”)。
  • @David542 像 MySQL 这样的引擎更宽松,它们不仅接受布尔表达式,还接受条件中的数字表达式;我相信任何不同于零的结果都被解释为真实。但是,我会认为这是规则的一个例外,符合规范并且只考虑过滤谓词的布尔表达式可能更安全。
  • @David542 我希望我的解释能帮助你朝着正确的方向前进。写这篇文章真的让我很兴奋,在这个过程中我学到了很多 SQL 怪癖。

标签: sql parsing grammar context-free-grammar bnf


【解决方案1】:

语法不关心语义。

从句法上讲,表达式就是表达式,仅此而已。如果您稍后进行某种语义分析,那就是您需要处理差异的时候。您可以在 conditionfrom_expression 的缩减操作中这样做,但在解析时构建一个 AST 并稍后在树上进行语义分析会更简洁。

【讨论】:

    【解决方案2】:

    一个选项如下,内嵌示例:

    expression:
        literal                                       # 'abc', 1, 
        column                                        # Table.Field1, Table.Profit
        function call                                 # ABS(...)
        operator invocation                           # NOT field1, 2+3, Genres[0], Genres[1:2], Address.name
    
    condition:
        expression { = | != | < | <= | > | >= | IN } expression  # Revenue > 0
        expression IS [NOT] NULL                       # Revenue IS NOT NULL
        condition { AND | OR } condition               # Revenue > 0 AND Profit < 100
    

    【讨论】:

      【解决方案3】:

      这是一条不适合 cmets 部分的评论。

      我不愿提供 LiveSQL 实现的链接,因为它专门用于 Java 语言,它是开源的,但我们从未添加任何文档。阅读风险自负。

      主类是LiveSQL.java:在第 113 行你可以看到 select 子句的主要变体。它有许多变体,但这是允许开发人员根据需要包含尽可能多的结果集列(表达式)的变体:

        public SelectColumnsPhase<Map<String, Object>> select(
            final ResultSetColumn... resultSetColumns) {
          return new SelectColumnsPhase<Map<String, Object>>(this.sqlDialect,
              this.sqlSession, this.liveSQLMapper, false,
              resultSetColumns);
        }
      

      当然,SELECT 子句还有许多其他变体,如果您稍微探索一下,您可以在同一个类中找到它们。我认为当我研究所有变体时,我已经非常详尽了。它应该 [大部分] 完整,除了我没有考虑的非标准 SQL 方言变体。

      如果您在 WHERE 阶段跟踪 QueryBuilder,您可以看到谓词是如何在 SelectFrom.java 类的方法 where(final Predicate predicate)(第 101 行)中组装的,如下所示:

      public SelectWherePhase<R> where(final Predicate predicate) {
        return new SelectWherePhase<R>(this.select, predicate);
      }
      

      如您所见,WHERE 子句不接受任何类型的表达式。首先,它只接受一个表达式,而不是一个列表。其次,此表达式必须是 Predicate(布尔表达式)。当然,这个谓词可以任意复杂,混合各种表达式和布尔逻辑。您可以查看Predicate.java 类来探索所有可以构建的表达式。

      条件

      我们以上面描述的 Predicate 类为例。如果pq 是布尔表达式,而abc 是[大部分] 任何类型,您可以将条件表示为:

      condition:
        <predicate>
      
      predicate:
        p and q,
        p or q,
        not p,
        ( p ),
        a == b,
        a <> b,
        a > b,
        a < b,
        a >= b,
        a <= b,
        a between b and c,
        a not between b and c,
        a in (b, c, ... )
        a not in (b, c, ... )
      

      当然还有更多的操作符,但这会给你它的要点。

      【讨论】:

        猜你喜欢
        • 2019-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-08
        • 1970-01-01
        相关资源
        最近更新 更多