J'utilise Apache Spark DataFrames pour joindre deux sources de données et obtenir le résultat sous la forme d'un autre DataFrame. Je veux écrire le résultat dans une autre table Postgres. Je vois cette option:
myDataFrame.write.jdbc(url, table, connectionProperties)
Mais, ce que je veux faire, c'est UPSER la trame de données dans la table en fonction de la clé primaire de la table. Comment procéder? J'utilise Spark 1.6.0.
Il n'est pas pris en charge. DataFrameWriter
peut s'ajouter ou écraser la table existante. Si votre application nécessite une logique plus complexe, vous devrez traiter cela manuellement.
Une option consiste à utiliser une action (foreach
, foreachPartition
) avec une connexion JDBC standard. Une autre consiste à écrire dans un fichier temporaire et à gérer le reste directement dans la base de données.
Voir aussi SPARK-19335 ( Spark devrait prendre en charge la réalisation d'un DataFrame Upsert efficace via JDBC ) et les propositions connexes.
KrisP en a le droit. La meilleure façon de faire un upsert n'est pas par une déclaration préparée. Il est important de noter que cette méthode insérera une à la fois avec autant de partitions que le nombre de travailleurs dont vous disposez. Si vous voulez le faire en lot, vous pouvez aussi
import Java.sql._
dataframe.coalesce("NUMBER OF WORKERS").mapPartitions((d) => Iterator(d)).foreach { batch =>
val dbc: Connection = DriverManager.getConnection("JDBCURL")
val st: PreparedStatement = dbc.prepareStatement("YOUR PREPARED STATEMENT")
batch.grouped("# Of Rows you want per batch").foreach { session =>
session.foreach { x =>
st.setDouble(1, x.getDouble(1))
st.addBatch()
}
st.executeBatch()
}
dbc.close()
}
Cela exécutera des lots pour chaque travailleur et fermera la connexion DB. Il vous permet de contrôler le nombre de travailleurs, le nombre de lots et vous permet de travailler dans ces limites.
Si vous allez le faire manuellement et via l'option 1 mentionnée par zero323, vous devriez jeter un œil à Spark code source de l'instruction d'insertion ici
def insertStatement(conn: Connection, table: String, rddSchema: StructType): PreparedStatement = {
val columns = rddSchema.fields.map(_.name).mkString(",")
val placeholders = rddSchema.fields.map(_ => "?").mkString(",")
val sql = s"INSERT INTO $table ($columns) VALUES ($placeholders)"
conn.prepareStatement(sql)
}
PreparedStatement
est partie de Java.sql
et il a des méthodes comme execute()
et executeUpdate()
. Vous devez toujours modifier le sql
en conséquence, bien sûr.
Pour insérer JDBC, vous pouvez utiliser
dataframe.write.mode(SaveMode.Append).jdbc(jdbc_url,table_name,connection_properties)
En outre, Dataframe.write vous donne un DataFrameWriter et il a quelques méthodes pour insérer le dataframe.
def insertInto(tableName: String): Unit
Insère le contenu du DataFrame dans la table spécifiée. Il nécessite que le schéma du DataFrame soit le même que le schéma de la table.
Parce qu'il insère des données dans une table existante, le format ou les options seront ignorés.
http://spark.Apache.org/docs/latest/api/scala/index.html#org.Apache.spark.sql.DataFrameWriter
Rien encore à mettre à jour les enregistrements individuels hors de la boîte de spark cependant