Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Zgrabna dynamiczna groupby

Oto obejście dla Slick 3.2.3 (i trochę informacji o moim podejściu):

Być może zauważyłeś dynamiczne wybieranie kolumny są łatwe, o ile możesz przyjąć stały typ, np.: columnNames = List("col1", "col2") tableQuery.map( r => columnNames.map(name => r.column[String](name)) )

Ale jeśli spróbuj podobnego podejścia z groupBy operacji, Slick będzie narzekał, że "does not know how to map the given types" .

Tak więc, chociaż nie jest to eleganckie rozwiązanie, możesz przynajmniej zaspokoić bezpieczeństwo typu Slick, definiując statycznie oba:

  1. groupby typ kolumny
  2. Górna/dolna granica ilości groupBy kolumny

Prostym sposobem na zaimplementowanie tych dwóch ograniczeń jest ponowne założenie stałego typu i rozgałęzienie kodu na wszystkie możliwe ilości groupBy kolumny.

Oto w pełni działająca sesja Scala REPL, aby dać Ci pomysł:

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)

To jest brzydkie, gdy możesz mieć więcej niż kilka groupBy kolumny (tj. posiadające oddzielny if oddział dla 10+ przypadków stałby się monotonny). Mam nadzieję, że ktoś wejdzie i zmodyfikuje tę odpowiedź, aby ukryć ten szablon za jakimś cukrem składniowym lub warstwą abstrakcji.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. pobieranie danych z tabeli za pomocą jednego zapytania

  2. Wywołanie niezdefiniowanej metody Illuminate\\Database\\Schema\\Blueprint::increments()

  3. W jaki sposób można zaprojektować prostą relacyjną relacyjną bazę danych nauczyciel-przedmiot-uczeń-wsadowy?

  4. dane php z bazy danych nie są wyświetlane po wyszukiwaniu

  5. Jaki jest maksymalny rozmiar int(10) w Mysql