例如,我有以下表格定义:
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中有更好的方法来进行这样的聚合查询吗?
答案 0 :(得分:8)
首先,这种查询在纯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
现在可以将其转换为光滑的
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
但是我在当前版本的光滑版本中的其他查询中使用的聚合查询存在问题。
但是查询可以使用EXISTS
在没有GROUP BY的情况下编写SELECT r.* FROM Rooms r
WHERE NOT EXISTS (
SELECT r2.id FROM Rooms r2 WHERE r2.size > r.size and r2.houseId = r.houseId)
这可以再次转换为光滑的
val query = for {
r <- Rooms
if !Query(Rooms).filter(_.houseId === r.houseId).filter(_.size > r.size).exists
} yield r
另一种选择可能是使用window functions,但我无法真正帮助你,我不认为光滑可以与他们合作。
(请注意,我手头没有scala编译器,因此代码中可能存在错误)