2016-12-12 50 views
0

我一直在使用油滑,我碰到一個我似乎無法解決的問題。 我想要做的是跨多個表連接,Slick似乎直接將連接轉換爲嵌套選擇,我不認爲這是有效的。連接成爲嵌套選擇

例如,這是我使用,使加入的各種功能:

def partialReviewByPlaceIds(q: ReviewQuery, placeIds: Long*)(implicit psDAO: PlaceStatusDAO, upDAO: UserProfileDAO) = for { 
    ((r, up), ps) <- withUP(q) join psDAO.PlaceStatuses /*.filter(_.placeId.inSet(placeIds))*/ on { 
     case ((r, _), ps) => r.providerPlaceId === ps.providerSpecId && ps.provider === r.provider 
    } if ps.placeId.inSet(placeIds) 
    } yield (r, up) 

而且withUP樣子:

def withUP(q: ReviewQuery) = for { 
    (r, a) <- q join upDAO.UserProfiles on ((r, u) => r.providerUserId === u.providerUserId && r.provider === u.provider) 
    } yield (r, a) 

,這將直接轉化爲SQL:

SELECT 
    x2.x3, 
    x2.x4, 
    x2.x5, 
    x2.x6, 
    x2.x7, 
    x8.`url`, 
    x2.x9, 
    x2.x10, 
    x8.`provider_user_id`, 
    x2.x11, 
    x8.`id`, 
    x2.x12, 
    x8.`provider`, 
    x2.x13, 
    x2.x14, 
    x2.x15, 
    x8.`name`, 
    x2.x16, 
    x8.`image_url`, 
    x2.x17 
FROM (SELECT 
     `created`   AS x14, 
     `done`    AS x10, 
     `favourite`   AS x15, 
     `url`    AS x5, 
     `text`    AS x17, 
     `provider`   AS x7, 
     `provider_review_id` AS x4, 
     `id`     AS x16, 
     `read`    AS x13, 
     `provider_place_id` AS x6, 
     `rating`    AS x3, 
     `provider_user_id` AS x9, 
     `flagged`   AS x12, 
     `language`   AS x11 
     FROM `review` 
     ORDER BY `created` DESC) x2, `user_profile` x8, `place_status` x18 
WHERE ((x18.`place_id` IN (17)) AND ((x2.x9 = x8.`provider_user_id`) AND (x2.x7 = x8.`provider`))) AND 
     ((x2.x6 = x18.`provider_place_id`) AND (x18.`provider` = x2.x7)) 
LIMIT 0, 21 

什麼是更好的方法來解決這個問題? (我不加入的模式,因爲我不認爲它一定需要的情況&可以看查詢統計出來。)

編輯 當我編輯partialReviewByPlaceIds方法做一切都在爲理解而不是:

def partialReviewByPlaceIds(q: ReviewQuery, placeIds: Long*)(implicit psDAO: PlaceStatusDAO, upDAO: UserProfileDAO) = for { 
    ((r, up), ps) <- Reviews join upDAO.UserProfiles on ((r, up) => r.providerUserId === up.providerUserId && r.provider === up.provider) join psDAO.PlaceStatuses /*.filter(_.placeId.inSet(placeIds))*/ on { 
     case ((r, _), ps) => r.providerPlaceId === ps.providerSpecId && ps.provider === r.provider 
    } if ps.placeId.inSet(placeIds) 
    } yield (r, up) 

我得到一個平坦的查詢像:

SELECT 
    x2.`favourite`, 
    x3.`image_url`, 
    x2.`provider_place_id`, 
    x2.`id`, 
    x2.`rating`, 
    x2.`provider_user_id`, 
    x2.`provider`, 
    x3.`id`, 
    x3.`provider_user_id`, 
    x2.`done`, 
    x2.`flagged`, 
    x2.`language`, 
    x3.`provider`, 
    x2.`url`, 
    x2.`text`, 
    x2.`provider_review_id`, 
    x2.`read`, 
    x2.`created`, 
    x3.`name`, 
    x3.`url` 
FROM `review` x2, `user_profile` x3, `place_status` x4 
WHERE 
    ((x4.`place_id` IN (17)) AND ((x2.`provider_user_id` = x3.`provider_user_id`) AND (x2.`provider` = x3.`provider`))) 
    AND ((x2.`provider_place_id` = x4.`provider_place_id`) AND (x4.`provider` = x2.`provider`)) 
LIMIT 0, 21 

在我看來,Slick在查詢操作時會立即將查詢編譯爲SQL,如對查詢進行排序和過濾 - 這使得查詢很難組合在一起。

例如,可以期望通過review.created進行排序,而不是在子查詢中,但在聯接中。

播放框架2.5.8

播放光滑2.0.2

+0

需要考慮的事情 - 如果您得到了您想要的結果,我會解釋分析結果,並使用手工連接比較「理想」語法。在很多情況下,我發現性能是相同的。 – easel

回答

1

是的,你的懷疑是正確的 - 你的地方sortBy/filter事項。

您可能需要將它們提取到不同的方法,並可能組成它們的方法,而不是完全查詢filter/sortBy

def partialReviewByPlaceIds(q: ReviewQuery, placeIds: Long*)(implicit psDAO: PlaceStatusDAO, upDAO: UserProfileDAO) = for { 
    ((r, up), ps) <- Reviews join upDAO.UserProfiles on ((r, up) => matchingFilters(r, up)) join psDAO.PlaceStatuses /*.filter(_.placeId.inSet(placeIds))*/ on { 
     case ((r, _), ps) => r.providerPlaceId === ps.providerSpecId && ps.provider === r.provider 
    } if ps.placeId.inSet(placeIds) 
    } yield (r, up) 


def matchingFilter(r: Reviews, up: UserProfiles): Rep[Boolean] = { 
    r.providerUserId === up.providerUserId && r.provider === up.provider 
} 

我也建議滿一元符號(連接)解開這個查詢一點點:

def partialReviewByPlaceIds(q: ReviewQuery, placeIds: Long*)(implicit psDAO: PlaceStatusDAO, upDAO: UserProfileDAO) = for { 
    r <- Reviews 
    up <- upDao.UserProfiles if matchingFilters(r, up) 
    ps <- psDAO.PlaceStatuses if r.providerPlaceId === ps.providerSpecId && ps.provider === r.provider && ps.placeId.inSet(placeIds) 
} yield (r, up) 

def matchingFilter(r: Reviews, up: UserProfiles): Rep[Boolean] = { 
    r.providerUserId === up.providerUserId && r.provider === up.provider 
} 

你也可以提取這些join條件foreinKey秒 - 這應該使它甚至更簡潔和可重用,例如

def profile = foreignKey("fk_review_profile", (providerUserId, provider), UserProfiles)(p => (p.providerUserId, p.provider)) 

,那麼你將有:

def partialReviewByPlaceIds(q: ReviewQuery, placeIds: Long*)(implicit psDAO: PlaceStatusDAO, upDAO: UserProfileDAO) = for { 
    r <- Reviews 
    up <- r.profile 
    ps <- psDAO.PlaceStatuses if r.providerPlaceId === ps.providerSpecId && ps.provider === r.provider && ps.placeId.inSet(placeIds) 
} yield (r, up) 

你可以做同樣的第二個加入。希望這可以幫助你以不同的方式編寫查詢(以避免這些嵌套連接)。

+0

感謝您的回答。雖然這仍然不會真正導致連接,但是我遵循所說的內容後得到的結果是:https://gist.github.com/asheshambasta/08ba77b85d12a4cfb130a64984223a92這仍然不是我想要的。 – Ashesh

+1

呃......這是'JOIN'的一種形式。你可以做'JOIN'或'WHERE'和id比較。這裏相關的答案:http://stackoverflow.com/questions/121631/inner-join-vs-where –