【问题标题】:How can I do aggregate queries in Slick?如何在 Slick 中进行聚合查询?
【发布时间】:2013-07-22 09:22:11
【问题描述】:

例如,我有以下表定义:

object Houses extends Table[Long]("Houses") {
  def id = column[Long]("id")
  def * = id
}
object Rooms extends Table[(Long, Long)]("Rooms") {
  def id = column[Long]("id")
  def houseId = column[Long]("houseId")
  def size = column[Int]("size")
  def * = id ~ houseId ~ size
}

我想为每个房子选择最大的房间。

我想出了以下技巧:

val query = {
  (r1, r2) <- Rooms leftJoin Rooms on ((r1,r2) =>
    r1.houseId === r2.houseId && r1.size > r2.size
  )
  if r2.id.isNull
} yield r1

它可以满足我的需求,但它很丑,完全不可读,而且似乎会影响性能。我尝试在查询中使用groupBy,但似乎我误解了一些核心概念 - 我无法正确输入类型。

有没有更好的方法在 Slick 中进行这种聚合查询?

【问题讨论】:

    标签: scala scala-2.10 slick


    【解决方案1】:

    首先,这种查询在普通 SQL 中并不完全简单。 Slick groupBy 最终会转换为 SQL GROUP BY,所以要使用它,我们需要一个带有 GROUP BY 的 SQL 查询

    一个这样的查询可能看起来像

    SELECT r2.* FROM 
      (SELECT r.houseId, MAX(size) as size FROM Rooms r GROUP BY r.houseId) mx
      INNER JOIN
      Rooms r2 on r2.size = mx.size and r2.houseId = mx.houseId
    

    现在可以翻译成 slick

    val innerQuery = Query(Rooms).groupBy(_.houseId).map {
      case (houseId, rows) => (houseId, rows.map(_.size).max)
    }
    
    val query = for {
      (hid, max) <- innerQuery
      r <- Rooms if r.houseId === hid && r.size === max
    } yield r
    

    但是,在当前版本的 slick 中,我在其他查询中使用的聚合查询存在问题。

    但是查询可以在没有 GROUP BY 的情况下使用 EXISTS 编写

    SELECT r.* FROM Rooms r 
      WHERE NOT EXISTS (
        SELECT r2.id FROM Rooms r2 WHERE r2.size > r.size and r2.houseId = r.houseId)
    

    这又可以翻译成 slick

    val query = for {
      r <- Rooms
      if !Query(Rooms).filter(_.houseId === r.houseId).filter(_.size > r.size).exists
    } yield r
    

    另一种选择可能是使用window functions,但我无法真正帮助您,而且我认为 slick 无法使用它们。

    (请注意,我手头没有 scala 编译器,因此代码中可能存在错误)

    【讨论】:

    • 谢谢!带有exists 的版本确实要快得多,并且遍历的行数减少了 30 倍。
    猜你喜欢
    • 1970-01-01
    • 2018-11-11
    • 2013-02-17
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 1970-01-01
    • 2022-11-08
    相关资源
    最近更新 更多