在Scala ActiveRecord中使用groupby编写联接查询

时间:2019-04-04 19:39:41

标签: scala join activerecord group-by

我正在尝试在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查询时,将返回预期结果。

1 个答案:

答案 0 :(得分:4)

scala-activerecord dsl是基于squeryl的,因此,当找出一个复杂的查询时,我们可以降至squeryl级别,并使用其statement工具来漂亮地打印SQL语句。这样,我们可以迭代tsl每周一次,直到获得所需的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中。这种方法的潜在优势在于,似乎可以轻松获取更多文档。注意它在Group and Aggregate Queries上的内容,对于scala-activerecord也应间接适用:

  

Squeryl与SQL稍有不同,因为集合函数不是   在选择范围内允许。相反,它们在“计算”中声明   子句实际上是变相的选择,因为它的参数结尾   在生成的SQL的select子句中。这样做的动机   设计的选择是使编写无效的Select更加困难   语句,因为 DSL强制使用“ compute”子句来替换   选择或跟随组。

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

关于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))
)