web-dev-qa-db-fra.com

Spark Dataframes UPSERT to Postgres Table

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.

15
void

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.

18
zero323

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.

13
jstuartmill

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.

8
KrisP

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

2
Soumitra