2013-07-22 9 views
8

Na przykład, mam następujące definicje tabeli:Jak mogę wykonywać zapytania zbiorcze w środowisku Slick?

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 
} 

I chcę, aby wybrać największy pokój dla każdego domu.

wymyśliłem następującą sztuczkę:

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

Robi to co muszę, ale jest brzydki, całkowicie nieczytelny, i wydaje się zranić wydajności. Próbowałem użyć groupBy w zapytaniu, ale wydaje mi się, że nie rozumiem jakiejś podstawowej koncepcji - nie mogę uzyskać prawidłowych typów.

Czy istnieje lepszy sposób na wykonanie takiego zapytania zbiorczego w aplikacji Slick?

Odpowiedz

8

Po pierwsze, tego rodzaju zapytanie nie jest całkowicie proste w prostym SQL. Slick GroupBy przekłada się na SQL GROUP BY w końcu, tak aby używać go potrzebujemy zapytania SQL z GROUP BY

Jedna taka kwerenda mogłaby wyglądać

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 

To może teraz być tłumaczone na śliski

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 

Jednak miałem problemy z zagregowanymi zapytaniami używanymi w innych zapytaniach w aktualnej wersji śliskiej.

Ale zapytanie może być napisany bez grupy za pomocą 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) 

ten może być ponownie przetłumaczony na śliskim

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

Innym rozwiązaniem byłoby prawdopodobnie wykorzystanie window functions, ale mogę” Naprawdę ci w tym pomaga i nie sądzę, żeby firma Slick mogła z nimi pracować.

(Uwaga: nie mam pod ręką kompilatora scala, więc mogą występować błędy w kodzie)

+0

Dzięki! Wersja z 'exist' jest rzeczywiście znacznie szybsza i przechodzi przez 30x mniej wierszy. – Rogach