回答

收藏

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

技术问答 技术问答 230 人阅读 | 0 人回复 | 2023-09-14

我使用Slick 1.0.0-RC1.我对表对象有以下定义:% W6 p% A* b3 |
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行:
7 S' L% O8 M- t) ?8 JProductTable.filter(_.title === "something")  .sortBy(_.visitCounter)  .map(_.title)  .take(8)  .selectStatement输出为:
8 o) E) c* L7 u8 n. P, ]select x2.x3 from    (select x4.`title` as x3 from `products` x4      where x4.`title` = 'something       order by x4.`visitCounter` limit 8) x2如果我摆脱take()方法:' N8 c) @8 t: g8 E( E7 x7 j: }5 X
ProductTable.filter(_.title === "something") .sortBy(_.visitCounter) .map(_.title) .selectStatement所以输出是:
' j% F3 m# v1 P5 {select x2.`title` from `products` x2 where x2.`title` = 'something' order by x2.`visitCounter`所以我的问题是:当Slick使用查询对象take()method为什么构造时会产生一个子查询?( s0 X- G' G1 J9 |1 b
PS若可关联,则将MySql使用所有这些驱动程序9 n$ a/ w) w9 e/ B& ~/ o0 [
                                                               
4 C4 w) F8 I0 m  e5 @/ B    解决方案:                                                               
; F" Y( d1 h$ _& B  x                                                                有一个简短的答案,一个很长的答案。简短的是:子查询在那里,因为到目前为止还没有人费心删除。- ~5 S+ f( r5 L2 I
更长的答案与以下事实有关:交换 map”和“ take没有区别,因为查询的编译非常简单明了。1 Z$ t$ y8 c" F( }: S# c5 f! j5 a
与查询编译器相比,有一个 forceOuterBinds在这个阶段(潜在地)引入了许多额外的阶段Bind(aka; s, |. u& B! ]9 L2 G/ C) D; ~! ]# T' [3 _
flatMap)操作,这些操作在语义上是等效和冗余的。想法是取一些具有集合类型的 x    并将其转化为 Bind(s,x,Pure(s))    ,其中  X- y" Z0 T* J/ u
s    是新鲜的Symbol。如果 x    的形状已经是 了Pure(y)    ,把它变成
9 s$ x  ]+ u# [$ O5 RBind(s,Pure(ProductNode()),Pure(y))    。在Scala代码中将其视为 x:List [T]    转换为
6 q8 c3 K1 t; Y  r  e9 v, J+ ^2 bx.flatMap(s = > List(s))_或将 _List(y)    转换为 List(())。flatMap(s = > List(y/ K7 I* H+ y1 ^9 U
))。这种转换的目的是给我们一个位置修改投影(作为身份映射创建),以便在未来的编译阶段更容易重写树。/ m( j5 u1 y! u% Y% D# d
后来,在“convertToComprehensions,从一元形式(绑定、纯净、过滤、冒、滴等)转换 单独    修真节点(代表SQL  ]% Y7 P, F8 O+ p; |2 B
选择    句子)。结果仍然不合法SQL:SQL理解不是monad理解。它们的作用域规则非常有限,不允许 from    子句引用由先前的 from
7 f, e. }1 O2 d2 b# G; A8 j4 q子句引入的变量(在同一理解或封闭理解中)。
1 C) V& ~2 Y) z; j' [5 k3 T这就是为什么我们需要下一个阶段
3 u# j. w% {; k7 ufuseComprehensions乍一看,这似乎是一个完全的优化,但它实际上是生成正确代码所必需的。在这个阶段,尽量整合个人理解,以避免这些非法引用。我们在整合方面取得了一些进展,但没有100%解决示波器问题的方法(其实我很确定这是不可能的)。
$ w5 j( B' s$ Y重申一下,这个阶段的进展主要是由 正确性    的需求驱动,而不仅仅是生成更好的代码。那么我们能删除多余的子查询吗?是的,当然,但没有人实施。
! Q) _2 s. x( O7 }% T  i% }如果您想尝试实现此优化,请考虑以下注意事项:% w7 [0 v, Q& A0 q; X/ c% h
您是否满足于删除纯混合投影(即,选择    槽具有Some(ProductNode(ch))形式(其中ch理解每一个元素都是路径)?! Q# l- X/ K/ C! t1 W7 W2 Q
或者,也许你认为应该融合 从(… limit …))    中 选择x   1的方法    。你能用什么样的表达方式?RowNum可以吗?- O( L; _; S$ h2 e
子查询需要什么样的形状?例如,它可以包括 groupBy    或 orderBy    子句吗?
(还有一些问题我需要考虑:实现这种优化需要多长时间,而不是解释为什么没有优化?
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则