我使用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()
方法构造查询对象时会生成子查询?
P.S。如果它可以相关,我使用MySql驱动程序与所有这些
答案 0 :(得分:12)
有一个简短的答案和一个很长的答案。简短的一个是:子查询是存在的,因为到目前为止没有人愿意删除它。
更长的答案与交换“map”和“take”的事实无关,这是由于查询的编译完全是简单和直接的。
在查询编译器的早期,有一个“forceOuterBinds”阶段,它引入(可能)许多额外的Bind(a.k.a. flatMap)操作,这些操作在语义上是等效的和冗余的。我们的想法是采用一些具有集合类型的 x 并将其转换为 Bind(s,x,Pure(s)),其中 s 是一个新鲜的符号。如果 x 的形状已经是 Pure(y),我们将其转换为 Bind(s,Pure(ProductNode()),Pure(y))< / em>相反。在Scala代码中,将此视为将 x:List [T] 转换为 x.flatMap(s =&gt; List(s))或列表(y)进入 List(())。flatMap(s =&gt; List(y))。这种转换的目的是通过在我们可能想要的所有地方修改投影(创建为标识映射)来更容易地在后面的编译器阶段中进行树重写。
稍后,在“convertToComprehensions”中,来自monadic形式的所有节点(Bind,Pure,Filter,Take,Drop等)将单独转换为Comprehension节点(代表SQL 选择语句)。结果仍然不是合法的SQL:SQL理解不是monad理解。它们具有非常有限的范围规则,不允许 from 子句引用前一个从子句引入的变量(在相同的理解或封闭的理解中)。
这就是为什么我们需要下一个阶段“fuseComprehensions”,它可能看起来纯粹像乍看之下的优化,但实际上需要生成正确的代码。这个阶段试图尽可能地融合个人理解,以避免这些非法引用。我们已经取得了一些进展,我们可以融合但是100%的范围问题解决方案还没有实现(事实上,我很确定无法解决)。
重申一下,这一阶段的进展主要是由于正确性的需要,而不仅仅是生成更好的代码。那么我们可以删除那个额外的子查询吗?是的,当然,但还没有人实现它。
如果您想尝试实施此类优化,请注意以下几点:
(我需要考虑的事情:与解释为什么它还不存在相比,实施优化需要多长时间?)