2013-07-22 7 views
8

Zum Beispiel habe ich die folgende Tabelle Definitionen:Wie kann ich Abfragen in Slick aggregieren?

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 
} 

Und ich möchte für jedes Haus das größte Zimmer wählen.

kam ich mit dem folgenden Trick:

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

Es tut, was ich brauche, ist aber hässlich, völlig unleserlich, und scheint Leistung zu verletzen. Ich habe versucht, groupBy auf Abfrage zu verwenden, aber scheint, dass ich ein Kernkonzept falsch verstehe - ich kann die Arten nicht richtig erhalten.

Gibt es eine bessere Möglichkeit, solche Aggregatabfrage in Slick zu tun?

Antwort

8

Erstens ist diese Art der Abfrage in Plain SQL nicht ganz einfach. Slick groupBy übersetzt am Ende SQL GROUP BY, so dass es zu verwenden, benötigen wir eine SQL-Abfrage mit GROUP BY

Eine solche Abfrage aussehen könnte

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 

Dies kann nun glatt übersetzt werden

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 

Allerdings hatte ich Probleme mit aggregierten Abfragen in anderen Abfragen in der aktuellen Version von Slick verwendet.

Aber die Abfrage kann wahrscheinlich Verwendung von window functions Durch die Verwendung von 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) 

Dies kann wieder übersetzt glatt

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

eine weitere Option ohne GROUP geschrieben werden würde, aber ich kann‘ t help dir wirklich mit denen und ich glaube nicht, kann glatt mit ihnen arbeiten.

(Beachten Sie, dass ich keinen Scala-Compiler zur Hand habe, daher kann es Fehler im Code geben)

+0

Vielen Dank! Version mit 'exists' ist in der Tat viel schneller und durchläuft 30x weniger Zeilen. – Rogach

Verwandte Themen