8

たとえば、次のテーブル定義があります。

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 でこのような集計クエリを実行するより良い方法はありますか?

4

1 に答える 1

8

まず、この種のクエリはプレーン SQL では完全に単純ではありません。洗練された groupBy は最終的に SQL GROUP BY に変換されるため、これを使用するには GROUP BY を使用した SQL クエリが必要です。

そのようなクエリの1つは次のようになります

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

ただし、現在のバージョンの slick では、他のクエリで使用される集計クエリに問題がありました。

ただし、クエリは 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の使用でしょうが、私はそれらについてあなたを本当に助けることはできません.slickはそれらで動作するとは思いません.

(手元にscalaコンパイラがないため、コードにエラーがある可能性があることに注意してください)

于 2013-07-22T09:53:43.687 に答える