【问题标题】:Joins become nested selects连接变成嵌套选择
【发布时间】:2016-12-12 20:05:24
【问题描述】:

我一直在使用 Slick,但遇到了一个我似乎无法解决的问题。 我想要做的是跨多个表连接,而 Slick 似乎直接将连接转换为嵌套选择,我认为这不是那么有效。

例如,这是我用来进行连接的 kind 函数:

  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 理解中执行所有操作时:

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)

我得到一个 flatter 查询,例如:

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

【问题讨论】:

  • 需要考虑的事情——如果你得到你想要的结果,我会解释分析结果并使用手动连接将其与“理想”语法进行比较。在许多情况下,我发现性能是相同的。

标签: scala slick


【解决方案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 条件提取到foreinKeys - 这应该使其更加简洁和可重用,例如

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)

你可以对第二次加入做同样的事情。希望这可以帮助您以不同的方式编写查询(以避免这些嵌套连接)。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 2013-03-19
  • 1970-01-01
相关资源
最近更新 更多