2013-07-22 44 views
8

Ví dụ, tôi có các định nghĩa bảng sau:Tôi làm cách nào để thực hiện các truy vấn tổng hợp trong 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 
} 

Và tôi muốn chọn phòng lớn nhất đối với mỗi ngôi nhà.

tôi đã đưa ra cách lừa sau:

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

Nó không những gì tôi cần, nhưng là xấu xí, hoàn toàn không thể đọc được, và dường như làm tổn thương thực hiện. Tôi đã cố gắng sử dụng groupBy trên truy vấn, nhưng có vẻ như tôi hiểu nhầm một số khái niệm cốt lõi - Tôi không thể có được loại quyền.

Có cách nào tốt hơn để thực hiện truy vấn tổng hợp như vậy trong Slick không?

Trả lời

8

Đầu tiên, loại truy vấn này không hoàn toàn đơn giản trong SQL thuần túy. groupby Slick dịch để SQL GROUP BY cuối cùng, vì vậy sử dụng nó chúng ta cần một truy vấn SQL với GROUP BY

Một trong những truy vấn có thể trông giống như

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 

này bây giờ có thể được dịch sang trơn

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 

Tuy nhiên, tôi đã gặp sự cố với truy vấn tổng hợp được sử dụng trong các truy vấn khác trong phiên bản hiện tại của bóng bẩy.

Nhưng các truy vấn có thể được viết mà không có một GROUP BY sử dụng 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) 

này một lần nữa có thể được dịch sang trơn

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

Một lựa chọn khác có lẽ sẽ sử dụng window functions, nhưng tôi có thể' t thực sự giúp bạn với những người và tôi không nghĩ rằng slick có thể làm việc với họ.

(Lưu ý rằng tôi không có trình biên dịch scala trong tầm tay để có thể có lỗi trong mã)

+0

Cảm ơn! Phiên bản có 'tồn tại' thực sự nhanh hơn nhiều và đi qua các hàng ít hơn 30x. – Rogach

Các vấn đề liên quan