Slick多列聚合分组:如何在Scala中实现指定SQL查询
在Scala Slick中实现分组聚合多列的解决方案
嗨,我来帮你搞定这个Slick的分组聚合需求!先从基础的表映射开始,一步步实现你要的SQL查询效果。
首先假设你已经定义了USERS表的Slick映射类,大概长这样(可以根据你实际的字段类型调整):
import slick.jdbc.MySQLProfile.api._ import java.time.LocalDateTime class Users(tag: Tag) extends Table[(Long, LocalDateTime, String, String)](tag, "USERS") { def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) def createdAt = column[LocalDateTime]("CREATED_AT") def name = column[String]("NAME") def email = column[String]("EMAIL") override def * = (id, createdAt, name, email) } val users = TableQuery[Users]
接下来就是核心的分组聚合查询代码,完美对应你给出的SQL逻辑:
// 定义一个case class来接收查询结果(可选,但让结果更易处理) case class GroupedUser(id: Long, createdAt: LocalDateTime, name: String, email: String) val query = users .groupBy(_.email) // 按EMAIL分组,对应SQL的GROUP BY EMAIL .map { case (email, group) => ( group.map(_.id).max, // 聚合MAX(ID) group.map(_.createdAt).max, // 聚合MAX(CREATED_AT) group.map(_.name).max, // 处理NAME列(细节看下面的解释) email // 分组键EMAIL ) } .mapResult { case (maxId, maxCreatedAt, name, email) => // 把Option类型的聚合结果转为具体值,可根据业务用getOrElse处理空值 GroupedUser(maxId.get, maxCreatedAt.get, name.get, email) }
关键细节说明
groupBy(_.email):和SQL里的GROUP BY EMAIL完全对应,将数据按邮箱分组。- 聚合函数的使用:
group.map(_.字段).max就是Slick对应SQL聚合函数MAX(字段)的写法,同理你也可以用min、sum等其他聚合函数。 - 关于NAME列的注意事项:你原SQL里直接SELECT NAME,但在标准SQL规范中,非分组列如果不在聚合函数里会触发语法错误(比如MySQL开启
ONLY_FULL_GROUP_BY模式时)。这里我用group.map(_.name).max来规避问题,你也可以换成min;如果NAME和EMAIL是一一对应的(比如一个邮箱唯一绑定一个用户名),也可以把NAME加入分组条件,改成groupBy(u => (u.email, u.name)),这样就能直接取name而不用聚合。
执行查询获取结果
如果要运行这个查询并拿到数据,可以这样做:
import scala.concurrent.Await import scala.concurrent.duration._ // 替换成你的数据库配置 val db = Database.forConfig("yourDatabaseConfig") // 执行查询,等待10秒获取结果 val result = Await.result(db.run(query.result), 10.seconds) // 打印结果示例 result.foreach(println)
内容的提问来源于stack exchange,提问作者Igor Soto Campos
相关产品推荐
相关产品推荐

