当前位置: 首页 > 面试题库 >

为什么在调用take()方法时Slick会生成子查询

谭高峯
2023-03-14
问题内容

我使用Slick 1.0.0-RC1。我对表对象有以下定义:

object ProductTable extends Table[(Int, String, String, String, Double, java.sql.Date, Int, Option[Int], Int, Boolean)]("products") {
  def id = column[Int]("productId", O.PrimaryKey, O.AutoInc)
  def title = column[String]("title")
  def description = column[String]("description")
  def shortDescription = column[String]("shortDescription")
  def price = column[Double]("price")
  def addedDate = column[java.sql.Date]("addedDate")
  def brandId = column[Int]("brandId")
  def defaultImageId = column[Option[Int]]("defaultImageId")
  def visitCounter = column[Int]("visitCounter")
  def archived = column[Boolean]("archived")
  def * = id ~ title ~ description ~ shortDescription ~ price ~ addedDate ~ brandId ~ defaultImageId ~ visitCounter ~ archived
}

我需要一个简单的查询,该查询从数据库中选择8行:

ProductTable.filter(_.title === "something")
  .sortBy(_.visitCounter)
  .map(_.title)
  .take(8)
  .selectStatement

输出为:

select x2.x3 from 
   (select x4.`title` as x3 from `products` x4 
     where x4.`title` = 'something' 
     order by x4.`visitCounter` limit 8) x2

如果我摆脱take()方法:

ProductTable.filter(_.title === "something")
 .sortBy(_.visitCounter)
 .map(_.title)
 .selectStatement

那么输出是:

select x2.`title` from `products` x2 
where x2.`title` = 'something' 
order by x2.`visitCounter`

所以我的问题是:当Slick的查询对象使用take()method构造时,为什么会生成一个子查询?

PS如果可以关联,则将MySql驱动程序与所有这些一起使用


问题答案:

有一个简短的答案,一个很长的答案。简短的是:子查询在那里,因为到目前为止没有人费心删除它。

更长的答案与以下事实有关:交换“ map”和“ take”没有任何区别,这是由于查询的编译非常简单明了。

相对早于查询编译器,存在一个“ forceOuterBinds”阶段,该阶段(潜在地)引入了许多额外的Bind(aka
flatMap)操作,这些操作在语义上是等效的和冗余的。想法是取一些具有集合类型的 x 并将其转换为 Bind(s,x,Pure(s)) ,其中
s 是新鲜的Symbol。如果 x 的形状已经是 Pure(y) ,则将其变成
Bind(s,Pure(ProductNode()),Pure(y)) 。在Scala代码中,将其视为将 x:List [T] 转换为
x.flatMap(s = > List(s))_或将 _List(y) 转换为 List(())。flatMap(s = > List(y
))
。这种转换的目的是通过在我们可能要执行的所有地方给我们一个修改投影(作为身份映射创建)的位置,使在以后的编译器阶段重写树变得更加容易。


后来,在“convertToComprehensions”,从一元形式(绑定,纯净,过滤,冒,滴等)的所有节点都转换 单独 修真节点(代表SQL
选择 语句)。结果仍然不是合法的SQL:SQL理解不是monad理解。它们具有非常有限的作用域规则,不允许 from 子句引用由先前的 from
子句引入的变量(在同一理解或封闭理解中)。

这就是为什么我们需要下一个阶段“
fuseComprehensions”,乍一看似乎完全是一个优化,但实际上是生成正确代码所必需的。此阶段尝试尽可能地融合个人理解,以避免这些非法引用。我们在融合方面取得了一些进展,但是并没有100%解决示波器问题的方法(实际上,我很确定这是不可能解决的)。

重申一下,此阶段的进展主要是由对 正确性 的需求驱动的,而不仅仅是生成更好的代码。那么我们可以删除多余的子查询吗?是的,当然可以,但是还没有人实施。

如果您想尝试实现这种优化,请考虑以下注意事项:

  • 您是否满足于删除纯粹的混叠投影(即, 选择 槽具有Some(ProductNode(ch))形式(其中ch的每个元素都是路径)的理解)?
  • 或者,也许您认为还应该融合 从(… limit …))选择x + 1的方法 。您可以使用哪种表达方式?例如,RowNum可以吗?
  • 子查询需要什么样的形状?例如,它可以包含 groupByorderBy 子句吗?

(还有一些我要考虑的问题:与解释为什么尚不存在优化相比,实现该优化需要花费多长时间?)



 类似资料:
  • 我用java编写Rational类来做基本的数学运算,我想覆盖Number类和Comparable接口的方法。我这样做是为了双倍的价值 正如我们所知,BigInteger类也扩展了数字类,所以我对在doubleValue中调用哪个方法感到困惑,因为我已经覆盖了doubleValue

  • 问题内容: 我阅读的所有资料都提到了几个案例,并以“其他一些案例”作了总结。在视图/活动中调用onSaveInstanceState方法时,所有情况是什么? 问题答案: 该文档称 在活动被杀死之前将调用此方法,以便将来在将来返回某个时间时可以恢复其状态。

  • 问题内容: 我正在使用ant生成javadocs,但是一遍又一遍地获取此异常-为什么? 我正在使用JDK 1.6.0_06 版本。 问题答案: 看来这已被报告为Java错误。这似乎是由于使用了第三方库(例如JUnit)中的注释,而在Javadoc调用中不包含带有该注释的jar。 如果是这种情况,只需在javadoc上使用-classpath选项并包括额外的jar文件。

  • 问题内容: 我使用jdi接口创建调试器,并且当我使用MethodEntryRequests启用方法条目跟踪调试程序时,速度降低了数十倍。我为主线程设置了过滤器,并将挂起策略设置为SUSPEND_EVENT_THREAD。Classfilter是有限的,如果我打印任何接收到的事件,它显示的事件不会超过几十个,因此不应接收太多事件。我在本地调试,并且在调试后的java程序中使用了以下命令行: 问题答案

  • 问题内容: 假设我有一个查询发送到我的SQL-Server数据库,它花费了30秒钟以上,并且我的程序引发了SQL Query Timeout异常。查询是否仍然在我的数据库中徘徊,或者在引发异常后立即终止? 问题答案: 客户端使用注意事件向服务器发出查询超时信号。注意事件只是SQL Server客户端可以发送给它的TDS数据包的一种不同类型。除了连接/断开连接,T-SQL批处理和RPC事件外,客户端

  • 为什么mockito打电话为存根方法。 为什么它使一个实际调用func下当... then返回我已经检查了当做调试。