我一直在使用Slick而且我遇到了一个似乎无法解决的问题。 我想要做的是连接多个表,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
方法来代替for comprehension时所做的一切:
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
Play-slick 2.0.2
答案 0 :(得分: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
}
我还建议使用完整的monadic表示法(用于连接)来解决这个问题:
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)
你可以用第二次加入做同样的事情。希望这可以帮助您有点不同地编写查询(以避免这些嵌套连接)。