Slick 3.0 insertion ou mise à jour (upsert)
Quelle est la bonne façon de faire une insertOrUpdate en vrac dans Slick 3.0?
j'utilise MySQL où la requête appropriée serait
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
Voici mon code actuel qui est très lent :-(
// FIXME -- this is slow but will stop repeats, an insertOrUpdate
// functions for a list would be much better
val rowsInserted = rows.map {
row => await(run(TableQuery[FooTable].insertOrUpdate(row)))
}.sum
ce que je cherche est l'équivalent de
def insertOrUpdate(values: Iterable[U]): DriverAction[MultiInsertResult, NoStream, Effect.Write]
3 réponses
il y a plusieurs façons de rendre ce code plus rapide (chacune devrait être plus rapide que les précédentes, mais il devient progressivement moins idiomatique-slick):
-
Run
insertOrUpdateAll
au lieu deinsertOrUpdate
si sur slick-pg 0.16.1+await(run(TableQuery[FooTable].insertOrUpdateAll rows)).sum
-
lancez vos événements DBIO en une seule fois, plutôt que d'attendre que chacun s'engage avant de lancer le prochain:
val toBeInserted = rows.map { row => TableQuery[FooTable].insertOrUpdate(row) } val inOneGo = DBIO.sequence(toBeInserted) val dbioFuture = run(inOneGo) // Optionally, you can add a `.transactionally` // and / or `.withPinnedSession` here to pin all of these upserts // to the same transaction / connection // which *may* get you a little more speed: // val dbioFuture = run(inOneGo.transactionally) val rowsInserted = await(dbioFuture).sum
-
passez au niveau JDBC et lancez votre upsert en une seule fois ( idée via cette réponse ):
val SQL = """INSERT INTO table (a,b,c) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);""" SimpleDBIO[List[Int]] { session => val statement = session.connection.prepareStatement(SQL) rows.map { row => statement.setInt(1, row.a) statement.setInt(2, row.b) statement.setInt(3, row.c) statement.addBatch() } statement.executeBatch() }
comme vous pouvez le voir à exemples , vous pouvez utiliser la fonction ++=
pour insérer en utilisant JDBC lot insertion fonctionnalité. Par exemple:
val foos = TableQuery[FooTable]
val rows: Seq[Foo] = ...
foos ++= rows // here slick will use batch insert
vous pouvez également" Taille "Vous lot par "groupement" les lignes séquence:
val batchSize = 1000
rows.grouped(batchSize).foreach { group => foos ++= group }
utilisation sqlu
cette démonstration de travail
case ("insertOnDuplicateKey",answers:List[Answer])=>{
def buildInsert(r: Answer): DBIO[Int] =
sqlu"insert into answer (aid,bid,sbid,qid,ups,author,uid,nick,pub_time,content,good,hot,id,reply,pic,spider_time) values (${r.aid},${r.bid},${r.sbid},${r.qid},${r.ups},${r.author},${r.uid},${r.nick},${r.pub_time},${r.content},${r.good},${r.hot},${r.id},${r.reply},${r.pic},${r.spider_time}) ON DUPLICATE KEY UPDATE `aid`=values(aid),`bid`=values(bid),`sbid`=values(sbid),`qid`=values(qid),`ups`=values(ups),`author`=values(author),`uid`=values(uid),`nick`=values(nick),`pub_time`=values(pub_time),`content`=values(content),`good`=values(good),`hot`=values(hot),`id`=values(id),`reply`=values(reply),`pic`=values(pic),`spider_time`=values(spider_time)"
val inserts: Seq[DBIO[Int]] = answers.map(buildInsert)
val combined: DBIO[Seq[Int]] = DBIO.sequence(inserts)
DEST_DB.run(combined).onComplete(data=>{
println("insertOnDuplicateKey data result",data.get.mkString)
if (data.isSuccess){
println(data.get)
val lastid=answers.last.id
Sync.lastActor !("upsert",tablename,lastid)
}else{
//retry
self !("insertOnDuplicateKey",answers)
}
})
}
et j'essaie d'utiliser sqlu dans un seul sql mais erreur peut-être sqlu ne fournit pas de chaîne D'Interpolation
cette démo ne fonctionne pas
case ("insertOnDuplicateKeyError",answers:List[Answer])=>{
def buildSql(execpre:String,values: String,execafter:String): DBIO[Int] = sqlu"$execpre $values $execafter"
val execpre="insert into answer (aid,bid,sbid,qid,ups,author,uid,nick,pub_time,content,good,hot,id,reply,pic,spider_time) values "
val execafter=" ON DUPLICATE KEY UPDATE `aid`=values(aid),`bid`=values(bid),`sbid`=values(sbid),`qid`=values(qid),`ups`=values(ups),`author`=values(author),`uid`=values(uid),`nick`=values(nick),`pub_time`=values(pub_time),`content`=values(content),`good`=values(good),`hot`=values(hot),`id`=values(id),`reply`=values(reply),`pic`=values(pic),`spider_time`=values(spider_time)"
val valuesstr=answers.map(row=>("("+List(row.aid,row.bid,row.sbid,row.qid,row.ups,"'"+row.author+"'","'"+row.uid+"'","'"+row.nick+"'","'"+row.pub_time+"'","'"+row.content+"'",row.good,row.hot,row.id,row.reply,row.pic,"'"+row.spider_time+"'").mkString(",")+")")).mkString(",\n")
val insertOrUpdateAction=DBIO.seq(
buildSql(execpre,valuesstr,execafter)
)
DEST_DB.run(insertOrUpdateAction).onComplete(data=>{
if (data.isSuccess){
println("insertOnDuplicateKey data result",data)
//retry
val lastid=answers.last.id
Sync.lastActor !("upsert",tablename,lastid)
}else{
self !("insertOnDuplicateKey2",answers)
}
})
}
un outil de synchronisation mysql avec scala slick https://github.com/cclient/ScalaMysqlSync