【问题标题】:Write join query with groupby in Scala ActiveRecord在 Scala ActiveRecord 中使用 groupby 编写连接查询
【发布时间】:2019-04-04 19:39:41
【问题描述】:

我正在尝试在scala Active record 中编写特定查询。但它总是什么都不返回。我已经阅读了 github 页面上的 wiki,但它并没有包含很多信息。我要写的查询是

SELECT e.name, e.id, COUNT(pt.pass_id) as pass_count, e.start_date, e.total_passes_to_offer
FROM events e inner join passes p on e.id = p.event_id inner join pass_tickets pt on p.id = pt.pass_id where e.partner_id = 198 group by e.name, e.id

我试过的是

Event.joins[Pass, PassTicket](
                (event, pass, passTicket) => (event.id === pass.eventId, pass.id === passTicket.passId)
            ).where(
                (event, _, _) => event.partnerId === partnerId
            ).select(
                (event, pass, _) => (event.name, event.id, PassTicket.where(_.passId === pass.id).count, event.startDate, event.totalPassesToOffer)
            ).groupBy( data => data._2)

但是首先,返回类型变成了一个映射,而不是一个列表。其次,在执行时,即使数据存在,它也不会返回任何内容。直接对数据库运行 SQL 查询时,会返回预期的结果。

【问题讨论】:

  • 请在代码问题中给出minimal reproducible example--剪切&粘贴&可运行代码加上所需的输出加上清晰的规范和解释。缺少一个是关闭的理由。不幸的是,赏金系统的一个缺陷是无法关闭具有开放赏金的问题。但为了最大限度地提高您获得有用答案的机会,请相应地进行编辑。 PS既然你没有得到你所期望的,“SQL 执行得很好”是什么意思? PS什么github页面?
  • 我会尝试将这部分替换为 passTicket 忽略 (event, pass, _) => (event.name, event.id, PassTicket.where(_.passId === pass.id).count, event.startDate, event.totalPassesToOffer) 与实际参考,类似这样的 (event, pass, passTicket) => (event.name, event.id, passTicket.where(_.passId === pass.id).count, event.startDate, event.totalPassesToOffer)。我不知道这是否是正确的语法,这就是我发表评论而不是答案的原因。
  • @Shikkou 说“passTicket”只是一个表结构。所以你不能在上面调用方法。

标签: scala join activerecord group-by


【解决方案1】:

scala-activerecord dsl 基于squeryl,因此在计算复杂查询时,我们可以下拉到 squeryl 级别并使用其statement 工具来漂亮地打印 SQL 语句。这样,我们可以迭代地对 dsl 进行 tweek 处理,直到获得所需的 SQL 语句。例如,假设我们有以下架构:

object Tables extends ActiveRecordTables {
  val persons = table[Person]
  val tickets = table[Ticket]
}

case class Person(name: String, email: String, age: Int) extends ActiveRecord
case class Ticket(price: Float, priority: Boolean) extends ActiveRecord {
  lazy val person = belongsTo[Person]
}

object Person extends ActiveRecordCompanion[Person]
object Ticket extends ActiveRecordCompanion[Ticket]

然后我们使用 squeryl dsl 来定义以下查询

  val query =
    dsl.join(Person.toQuery, Ticket.toQuery)((person, ticket) =>
      groupBy(person.name, person.age)
      compute(count(ticket.id))
      on(person.id === ticket.id)
    )

然后我们可以使用漂亮的打印语句

println(Person.inTransaction(query.statement))

输出实际的 SQL 语句

Select
  q1.people6_name as g0,
  q1.people6_age as g1,
  count(q7.tickets11_id) as c0
From
(Select
   people6.name as people6_name,
   people6.email as people6_email,
   people6.age as people6_age,
   people6.id as people6_id
 From
   people people6
)  q1
 inner join (Select
   tickets11.priority as tickets11_priority,
   tickets11.price as tickets11_price,
   tickets11.id as tickets11_id
 From
   tickets tickets11
)  as q7 on (q1.people6_id = q7.tickets11_id)
Group By
  q1.people6_name,
  q1.people6_age

一旦我们在 squeryl 中找出正确的 dsl,那么我们至少知道它是可能的,然后我们可以尝试在 scala-activerecord 中也写它。这种方法的潜在优势是 squerly 似乎有更多的文档。请注意它在 Group and Aggregate Queries 上的声明,这对于 scala-activerecord 也应该间接适用:

Squeryl 与 SQL 略有不同,因为 聚合函数不是 允许在一个选择中。相反,它们在“计算”中声明 子句实际上是变相的选择,因为它的参数结束 在生成的 SQL 的 select 子句中。这样做的动机 设计选择是让编写无效的 Select 变得更加困难 语句,因为 DSL 强制“计算”子句替换 选择或关注 groupBy。

根据我的理解,这意味着我们不应该在select 子句中写PassTicket.where(_.passId === pass.id).count

关于groupBy 返回一个Map,我们可以调用values.toList 来取回列表,例如,假设我们有

  Person("Picard", "picard@starfleet.org", 34).save
  Person("Data", "data@starfleet.org", 40).save
  Person("Geordi", "geordi@starfleet.org", 40).save

那么println(Person.groupBy(person => person.age).values.toList)应该给

List(
  List(Person(Data,data@starfleet.org,40), Person(Geordi,geordi@starfleet.org,40)), 
  List(Person(Picard,picard@starfleet.org,34))
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-04
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-01
    • 1970-01-01
    • 2021-05-22
    相关资源
    最近更新 更多