在 slick+play 上使用 'HAVING' 子句分组

Group By with 'HAVING' clause on slick+play

假设我有一个 SQL table grades,其中包含学生姓名和成绩结果:

| student  |   grade   |
|----------|:---------:|
| Harry    |    Good   |
| Ron      |    Good   |
| Harry    |  Average  |
| Harry    |    Fail   |
| Hermione | Excellent |
| Hermione | Excellent |
| Ron      |  Average  |
| .....    |    ....   |

如果我想 select 所有至少有两个 'Excellent' 和零 'Fail' 成绩的学生可以做到:

select student
from grades
group by student
having 
      sum(case when grade = 'Excellent' then 1 else 0 end) >= 2 and
      sum(case when grade = 'Fail' then 1 else 0 end)

如何将这样的查询翻译成 Slick? 在 documentation 上,他们给出的 'Having' 子句似乎更简单。

gradesTables
.groupBy(._student)
.map{ case(student, group) => (student, ???)}
.filter(???)
.list

在相关说明中,为什么我会收到以下错误:

   gradesTables
    .groupBy(._student)
    .map{ case(student, group) => (student, group.filter(_.grade == "Fail").length)}
    .list

错误是:

slick.SlickTreeException: Cannot convert node to SQL Comprehension

Slick 中的以下代码将生成您需要的 SQL:

val query: Query[(Rep[String], Rep[Option[Int]], Rep[Option[Int]]), (String, Option[Int], Option[Int]), Seq] =
  grades.groupBy( _.student ).map{ case (student, group) => 
    val groupList = group.map(_.grade)

    val gradeExcel = groupList.map( grade =>
      Case.If(grade === "Excellent").Then(1).Else(0) ).sum
    val gradeFail = groupList.map( grade =>
      Case.If(grade === "Fail").Then(1).Else(0) ).sum

    (student, gradeExcel, gradeFail)
  }.
  filter( g => g._2 >= 2 && g._3 === 0 )

// ...

println("Generated SQL:\n" + query.result.statements)
// Generated SQL:
// List(
//   select "STUDENT", sum((case when ("GRADE" = 'Excellent') then 1 else 0 end)), 
//   sum((case when ("GRADE" = 'Fail') then 1 else 0 end)) from "GRADES" group by "STUDENT" 
//   having (sum((case when ("GRADE" = 'Excellent') then 1 else 0 end)) >= 2) and 
//   (sum((case when ("GRADE" = 'Fail') then 1 else 0 end)) = 0)
// )

db.run(query.result.map(println))
// Vector((Hermione,Some(2),Some(0)))