【问题标题】:How to express this query in slick involving group by, having, count如何在涉及 group by、have、count 的 slick 中表达此查询
【发布时间】:2018-10-18 13:42:39
【问题描述】:

假设我在 Slick 3.2.3 中有一个这样定义的表:

class ATable(tag: Tag) extends Table[(Int, Option[Boolean])](tag, "a_table") {
  def someInt = column[Int]("some_int")
  def someBool = column[Option[Boolean]]("some_bool")
  def * = (someInt, someBool)
}
object ATable extends TableQuery(new ATable(_))

还有一些数据:

insert into a_table
values
(1, true),
(2, null),
(2, true),
(2, null),
(3, true),
(3, true),
(3, null);

现在我想在表中找到那些整数,其中恰好有一行 some_bool 不是 null。这在 SQL 中并不难:

select some_int
from a_table
group by some_int
having count(some_bool) = 1;

这很好用。所以让我们用 Slick 试试吧:

ATable
 .groupBy(_.someInt)
 .filter(_._2.map(_.someBool).countDefined === 1)
 .map(_._1)

当它编译时,它会在运行时崩溃并显示错误消息“slick.SlickTreeException: Cannot convert node to SQL Comprehension”。这是已知的限制还是 slick 中的错误?还是我应该以不同的方式编写查询? 当然可以用子查询来写,但我更想明白为什么groupBy这个东西不起作用……

【问题讨论】:

标签: scala slick


【解决方案1】:

您的 SQL 的 Slick 等效项应该类似于以下示例中的 query

val aTable: TableQuery[ATable] = TableQuery[ATable]

val setupAction: DBIO[Unit] = DBIO.seq(
  aTable.schema.create,
  aTable += (1, Some(true)),
  aTable += (2, None),
  aTable += (2, Some(true)),
  aTable += (2, None),
  aTable += (3, Some(true)),
  aTable += (3, Some(true)),
  aTable += (3, None)
)

val setupFuture: Future[Unit] = db.run(setupAction)

val f = setupFuture.flatMap{ _ =>
  val query =
    aTable.groupBy(_.someInt).
      map{ case (someInt, group) => (someInt, group.map(_.someBool).countDefined) }.
      filter(_._2 === 1).
      map(_._1)

  println("Generated SQL for query:\n" + query.result.statements)
  db.run(query.result.map(println))
}

// Generated SQL for query:
// List(select "some_int" from "a_table" group by "some_int" having count("some_bool") = 1)
// Vector(1, 2)

【讨论】:

  • 嘿,感谢您的回复,这确实有效。这很奇怪,因为集合foo.map(a => (a, f(a))).filter(_._2).map(_._1) 等同于foo.filter(f),所以我很想将此称为错误。我还发现了另一个问题:如果您将group.map(_.someBool).countDefined 更改为group.map(_.someBool).distinct.length,它会再次中断。不幸的是,这实际上是我在现实世界用例中所需要的:-(
  • @Matthias Berndt,不幸的是我自己对 Slick 的groupBy 的体验也不是那么积极——例如,它只支持一组有限的聚合函数。用countDistinct 替换countDefined 将适用于您的情况,但您会收到deprecated 警告。显然,distinct.lengthdistinctOn(_.someBool) 都不能在 groupBy 中工作。
  • 我明白了,谢谢!这显然是一个错误,因为文档指出 .distinct.length 是你应该做的,而不是 .countDistinct。更糟糕的是,我还需要我计划使用 array_agg 获得的另一列中的值。但这也不起作用,因为 slick-pg 不支持使用带有 groupBy 的那些(slick-pg bug #289)。所以这一切都非常令人难过:-(。无论如何,谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 2011-03-27
  • 2010-09-19
  • 2020-08-15
  • 1970-01-01
相关资源
最近更新 更多