光滑:从groupBy“group”获取元素

时间:2014-07-12 12:44:09

标签: scala slick slick-2.0

从"组"中选择元素的首选方式是什么?小组的一部分? (groupBy导致[key - > group]关系)。如果可能的话,它应该导致一次db命中,或至少一次点击量。

举个例子,我们假设我们有一个Locations(id)和一个Companies(id,locationId,establishedDate)表。我们怎样才能找到每个地点最老的公司?

我会这样做的:

Locations.join(Companies).on(_.id === _.locationId).groupBy(_._1).flatMap {
  case (location, companies) =>
    companies.map(_._2).sortBy(_.foundedDate).take(1).map {
      company => (location, company)
    }
}

但这会产生运行时异常:

scala.slick.SlickException: Unsupported query shape containing .groupBy without subsequent .map
at scala.slick.compiler.FuseComprehensions.fuse(Relational.scala:200)
...

2 个答案:

答案 0 :(得分:1)

我不熟悉Scala或Slick,但如果它在底层使用SQL,那么这不会起作用。在大多数实现中,查询返回的元素需要是类别值之一(在这种情况下是位置和公司)或聚合函数。

这可能有用......

SELECT location.id, company.id, MAX(foundedDate)
FROM companies INNER JOIN locations ON (locations.id = companies.locationId)
GROUP BY location.id, company.id
ORDER BY location.id, MAX(foundedDate)

......但这在功能上等同于通过以下方式进行排序:

SELECT location.id, company.id, foundedDate
FROM companies INNER JOIN locations ON (locations.id = companies.locationId)
ORDER BY location.id, foundedDate

要查找最老的公司而不需要过滤结果,我们可以使用相关的子查询:

SELECT location.id, company.id, foundedDate
FROM companies INNER JOIN locations ON (locations.id = companies.locationId)
WHERE foundedDate = (SELECT MAX(foundedDate from companies c2 where c2.locationId = location.id)

如果他们的创建日期相同,则可以按位置返回多家公司。

如何将其映射回Scala / Slick,我无法说,但希望它有所帮助。

答案 1 :(得分:0)

Slick不会为此类查询生成最佳代码,如果您在此处查询此查询也是如此。 MySQL可能很重(如果同一地点有几家公司在同一天成立,可能会很模糊)。因此,更简单的解决方案可能是单独选择公司,按位置对它们进行分组,并按数据库外的日期排序,然后将它们与位置进行匹配。

val locations = Locations.list.map(location => (l.id,location)).asMap

val lcPairs = Companies.list
                  .group(_.locationId)
                  .map{ case (locationId, companies) => (
                      locations(id),companies.sort(_.foundedDate).head
                   )}