sql >> Databáze >  >> RDS >> Mysql

Slick dynamic groupby

Zde je řešení pro Slick 3.2.3 (a nějaké pozadí mého přístupu):

Možná jste si všimli dynamického výběru columns je snadné, pokud můžete předpokládat pevný typ, např.: columnNames = List("col1", "col2") tableQuery.map( r => columnNames.map(name => r.column[String](name)) )

Ale pokud zkusíte podobný přístup s groupBy Slick si bude stěžovat, že "does not know how to map the given types" .

Takže, i když se nejedná o elegantní řešení, můžete alespoň uspokojit typovou bezpečnost Slick tím, že staticky definujete obojí:

  1. groupby typ sloupce
  2. Horní/dolní mez množství groupBy sloupce

Jednoduchým způsobem implementace těchto dvou omezení je opět převzít pevný typ a větvit kód pro všechna možná množství groupBy sloupce.

Zde je úplná pracovní relace REPL pro Scala, abyste měli představu:

import java.io.File

import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import slick.jdbc.H2Profile.api._

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._


val confPath = getClass.getResource("/application.conf")
val config = ConfigFactory.parseFile(new File(confPath.getPath)).resolve()
val db = Database.forConfig("slick.db", config)

implicit val system = ActorSystem("testSystem")
implicit val executionContext = system.dispatcher

case class AnyData(a: String, b: String)
case class GroupByFields(a: Option[String], b: Option[String])

class AnyTable(tag: Tag) extends Table[AnyData](tag, "macro"){
  def a = column[String]("a")
  def b = column[String]("b")
  def * = (a, b) <> ((AnyData.apply _).tupled, AnyData.unapply)
}

val table = TableQuery[AnyTable]

def groupByDynamically(groupBys: Seq[String]): DBIO[Seq[GroupByFields]] = {
  // ensures columns are returned in the right order
  def selectGroups(g: Map[String, Rep[Option[String]]]) = {
    (g.getOrElse("a", Rep.None[String]), g.getOrElse("b", Rep.None[String])).mapTo[GroupByFields]
  }

  val grouped = if (groupBys.lengthCompare(2) == 0) {
    table
      .groupBy( cols => (cols.column[String](groupBys(0)), cols.column[String](groupBys(1))) )
      .map{ case (groups, _) => selectGroups(Map(groupBys(0) -> Rep.Some(groups._1), groupBys(1) -> Rep.Some(groups._2))) }
  }
  else {
    // there should always be at least one group by specified
    table
      .groupBy(cols => cols.column[String](groupBys.head))
      .map{ case (groups, _) => selectGroups(Map(groupBys.head -> Rep.Some(groups))) }
  }

  grouped.result
}

val actions = for {
  _ <- table.schema.create
  _ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a1", "b1")
  _ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b2")
  _ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b3")
  queryResult <- groupByDynamically(Seq("b", "a"))
} yield queryResult

val result: Future[Seq[GroupByFields]] = db.run(actions.transactionally)
result.foreach(println)

Await.ready(result, Duration.Inf)

Kde to začíná být ošklivé, je, když můžete mít více než několik groupBy sloupce (tj. mající samostatné if větev pro 10+ případů by byla monotónní). Doufejme, že se někdo vrhne a upraví tuto odpověď na to, jak skrýt tento vzor za nějaký syntaktický cukr nebo abstrakci.




  1. Naučte se, jak vytvořit PK z Sequence Trigger v SQL Developer

  2. Nelze se připojit k serveru MySQL na 'localhost' (10061)

  3. Zachovat časové pásmo v typu Timestamptz PostgreSQL

  4. Jak mohu předat uložená webová data localStorage do skriptu php?