Mysql
 sql >> база данни >  >> RDS >> Mysql

Изящно динамично групиране

Ето решение за Slick 3.2.3 (и малко предистория за моя подход):

Може да сте забелязали динамичното избиране колоните е лесно, стига да приемете фиксиран тип, напр.: columnNames = List("col1", "col2") tableQuery.map( r => columnNames.map(name => r.column[String](name)) )

Но ако опитайте подобен подход с groupBy операция, Slick ще се оплаче, че "does not know how to map the given types" .

Така че, въпреки че това едва ли е елегантно решение, можете поне да удовлетворите безопасността на типа на Slick, като статично дефинирате и двете:

  1. groupby тип колона
  2. Горна/долна граница на количеството groupBy колони

Прост начин за прилагане на тези две ограничения е отново да се приеме фиксиран тип и да се разклони кодът за всички възможни количества от groupBy колони.

Ето пълната работеща сесия на Scala REPL, за да ви даде идея:

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)

Това става грозно, когато можете да имате повече от няколко groupBy колони (т.е. с отделен if клон за 10+ случая ще стане монотонен). Надяваме се, че някой ще се впусне и ще редактира този отговор за това как да скриете този шаблон зад някакъв синтактичен захарен или абстракционен слой.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PHP функцията работи само веднъж

  2. Улавяне на грешно изключение

  3. laravel 5.6 групово вмъкване на json данни

  4. Непрекъснато мигриране на данни от mysql към Hbase

  5. Най-евтиният начин да определите дали MySQL връзка все още е жива