2016-01-23 7 views
5

Diese Frage ist glatt 3.0 oder 3.1 (Ich bin flexibel darüber)Scala + Slick 3: Einfügen das Ergebnis einer Abfrage in eine andere Tabelle

Ich habe eine Zwischenabfrage, die ich mit map verarbeiten, for, etc um das gewünschte Ergebnis zu erhalten. Am Ende habe ich eine

val foo: DBIOAction[Seq[MySchema.Bar], NoStream, Effect.Read] 

Jetzt habe ich ein val bar: TableQuery[MySchema.Bar] und ich möchte foo, um es einzufügen.

Wenn foo wäre ein Seq Ich könnte einfach tun bar ++= foo, aber es ist nicht.

Die einzige Möglichkeit, die ich gefunden habe, ist das Ergebnis materialisieren, indem Sie darauf warten. Gefallen Sie diesen

val query = (bar ++= Await.result(db.run(foo), Duration.Inf)) 

Offensichtlich brauchen query mit db.run an einem gewissen Punkt ausgeführt werden. Aber jetzt habe ich zwei DB-Läufe. Wäre es nicht besser, alles in einem Durchgang zu haben?

Gibt es einen besseren Weg, dies zu tun?

+0

'bar + = foo'? Aber du müsstest es immer noch 'db.run', egal ob es' + = 'oder' ++ = '... (?) Ist – kornfridge

+0

Ich denke ++ = mit Nebenwirkungen war die slick2 api, während die slick3 ist mehr funktional, also müssen Sie explizit eine 'db.run' ausführen. – kornfridge

+0

Ja, das Ergebnis von ++ = ist jetzt eine Abfrage, die ausgeführt werden muss. Ich habe die Frage in diesem Punkt bearbeitet. Aber ich muss zwei db.run Aufrufe für diese Aufgabe tun. – masgo

Antwort

4

DBIOAction hat map/flatMap Funktionen, so können Sie so etwas wie

val insertAction = foo.flatMap(bar ++= _) 

Die insertAction wird vom Typ DBIOAction[Int, NoStream, Effect.Write] oder so ähnlich (ich bin mir nicht ganz sicher Int und der Effekt) sein schreiben, dann können Sie es auf der DB als DBIOAction mit db.run; Was Sie dann bekommen, ist eine Zukunft der gesamten Abfrage und einfügen Ergebnis.

+0

Die 'flatMap' macht den Trick. Am Ende habe ich nur eine DBIOAction übrig, die ich ausführen muss. Ich bin mit diesem Thema auf eine andere Sache gestoßen: Wann sollte ich db.run() machen? Hier ist ein Beispiel: val foo = db.run (persons.result.map (_. map {p => tr (td (p.id))})) 'Ich kann es so machen, oder ich könnte den' db.run' Teil nach '.result beenden '. Beide Methoden geben mir die gleiche "Zukunft", gibt es einen Unterschied in Bezug auf die Leistung? – masgo

+2

Im Allgemeinen möchten Sie so weit wie möglich auf 'DBIO'-Ebene arbeiten; Wenn Sie 'db.run' aufrufen, wird slick so viele Abfragen ausführen, dass sie ihren eigenen Ausführungskontext verwenden, und Sie erhalten die' Zukunft' zurück. Ich denke, dass es einen potentiellen Unterschied in der Leistung gibt, wenn Sie dies tun, da Slick die Verwendung von Threads in Bezug auf DB-Zugriff optimieren wird. –

1

Die Fragen wurden bereits beantwortet, aber ich kam hierher, um nach einer anderen Lösung zu suchen, also wird dies vielleicht für andere hilfreich sein. Wie @ Aldo sagt, wollen wir so weit wie möglich auf der DBIO-Ebene arbeiten, aber ich würde weiter gehen und sagen, Sie sollten so weit wie möglich auf der Ebene Query arbeiten, da dies zu einer einzigen SQL-Anweisung kompiliert wird kann an die Datenbank gesendet werden.

Zum Beispiel sollte eine Einfügung von einer Auswahl zu einer INSERT INTO table1 SELECT... kompiliert werden. Wenn Sie mehrere DBIOS mit flatMap verwenden, wie vorgeschlagen, wird dies in eine SELECT kompiliert werden, die Werte werden in den Speicher gebracht und dann eine INSERT Anweisung kompiliert wird, die Werte in einer Zeichenfolge interpolieren und diese neue Abfrage an die gesendet wird Datenbank. In der Realität kann dies viel langsamer sein, wenn Ihre Auswahl viele Ergebnisse zurückgibt, und im schlimmsten Fall kann es Ihren Speicher entleeren.

So etwas wie dies in einer einzigen Abfrage kompilieren Sie schreiben können:

val bar: TableQuery[MySchema.Bar] 

val foo: Query[MySchema.Bar, Bar, Seq] 

val insert: DBIO[Int] = bar.forceInsertAll(foo)