sql >> Database teknologi >  >> RDS >> Mysql

Slim dynamisk gruppeaf

Her er en løsning til Slick 3.2.3 (og lidt baggrund om min tilgang):

Du har måske bemærket dynamisk valg kolonner er let, så længe du kan antage en fast type, f.eks.: columnNames = List("col1", "col2") tableQuery.map( r => columnNames.map(name => r.column[String](name)) )

Men hvis du prøver en lignende tilgang med en groupBy operation, vil Slick klage over, at den "does not know how to map the given types" .

Så selvom dette næppe er en elegant løsning, kan du i det mindste tilfredsstille Slicks typesikkerhed ved statisk at definere begge dele:

  1. groupby kolonnetype
  2. Øvre/nedre grænse for mængden af ​​groupBy kolonner

En simpel måde at implementere disse to begrænsninger på er igen at antage en fast type og at forgrene koden for alle mulige mængder af groupBy kolonner.

Her er hele den arbejdende Scala REPL-session for at give dig en idé:

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)

Hvor dette bliver grimt, er når du kan have op mod et par groupBy kolonner (dvs. have en separat if filial for 10+ tilfælde ville blive ensformig). Forhåbentlig vil nogen snuppe ind og redigere dette svar for, hvordan man skjuler den kedelplade bag et eller andet syntaktisk sukker- eller abstraktionslag.




  1. SQL Row_Number()-funktion i Where-klausul

  2. Kan du løse denne simple SQL-forespørgsel?

  3. Sådan automatiseres dataindsamling og ikke fryse på 10 %

  4. ORDER BY RAND()-funktionen tager lang tid at udføre i mysql