用scala实现一个sql执行引擎-(下)

执行

上一篇讲述了如何通过scala提供的内置DSL支持,实现一个可以解析sql的解析器,这篇讲如何拿到了解析结果-AST以后,如何在数据上进行操作,得到我们想要的结果。之前说到,为什么选择scala作为这个引擎的实现,之一是scala提供了方便的DSL实现支持,其二是因为作为一门函数式编程语言,scala提供了丰富对于集合操作的函数。此外,函数在scala中是一个独立的类型,所以能够把现有的函数进行组合,得到更为强大的函数(和上一篇提到的用解析组合子组合已有的解析器得到更强大的解析器一样)。

首先,我们需要弄清楚普通sql语句的执行顺序一般来说,sql的书写顺序为

  • SELECT[DISTINCT]
  • FROM
  • WHERE
  • GROUP BY
  • HAVING
  • UNION
  • ORDER BY

但是,执行顺序为

  • FROM
  • WHERE
  • GROUP BY
  • HAVING
  • SELECT
  • DISTINCT
  • UNION
  • ORDER BY

本次实现的sql执行不支持join,所以省去了union部分,但是大致顺序差不多,如果把一个List<Map>想象成一个数据库的表,那么执行顺序可以用下图所示,图中标绿色的箭头表示可以并发执行,聚合函数的执行是不能并发的,但是因为已经把数据给分组了,故可以在更高的一个层次并发。

了解了大致的执行流程,下面说一下各个流程执行所用到的函数。

 

  • where子句
    where子句的执行,利用了 filter函数,将不符合条件的数据给过滤掉。举个例子,下面这个段代码,将列表中的奇数给过滤掉。
    scala> val L = List(1,2,3,4,5,6)
    L: List[Int] = List(1, 2, 3, 4, 5, 6)
    scala> L filter(_%2==0)
    res2: List[Int] = List(2, 4, 6)

    对每个元素进行判断这个步骤其实是可以并发执行的,你只需要这样写,就能进行安全的并发操作。

    scala> L.par.filter(_%2==0).toList
    res4: List[Int] = List(2, 4, 6)

    在scala_sql引擎中,实现where的函数为

    def where(where: Option[SqlExpr]): Table = {
          where match {
            case None => table
            case Some(x: SqlExpr) => table filter (evalWhereEachRow(_, x))
          }
        }

    其中evalWhereEachRow(_,x)是另外一个函数,第一个参数是table中的一列,即一个Map对象,第二个参数是由sql解析得到的AST中,对应where子句的部分。

  • groupBy子句
    scala中也提供了对于集合的groupBy操作,接着上个例子
    scala> L groupBy(_%2)
    res6: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(1, 3, 5), 0 -> List(2, 4, 6))

    上面这个函数,把L这个list根据奇偶分组。
    在scala _sql引擎中,实现groupBy的函数为

     def evalGroupBy(table: Table, groupby: SqlGroupBy): Seq[Table] = {
        val keys: Seq[String] = groupby.keys map {
          case x: FieldIdent => x.name
        }
        table.groupBy(row => keys.map(row(_))).map(_._2).toSeq
      }

结论

DSL主要有两种,内部DSL和外部DSL,对于外部DSL,需要一个解析器来解析DSL的脚本,得到能够让程序处理的数据结构,这种数据结构通常是一种AST。实现需要的解析器,大体有两种方式:

  • 手工写一个,例如druid中的sql解析模块。
  • 利用ANTRL等解析生成器,通过编写语法规则,生成一个解析器。

近年来随着函数式编程慢慢得到工业界的关注,利用parser combinator(解析组合子)的方式来编写解析器实现DSL也进入了大众视野。

posted @   JavaNerd  阅读(2941)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示