web-dev-qa-db-fra.com

Spark SQL: Comment ajouter une nouvelle ligne à la table dataframe (à partir d'une autre table)

J'utilise Spark SQL avec des cadres de données. J'ai un cadre de données en entrée et j'aimerais ajouter (ou insérer) ses lignes à un cadre de données plus grand comportant davantage de colonnes. Comment je ferais ça? 

S'il s'agissait de SQL, j'utiliserais INSERT INTO OUTPUT SELECT ... FROM INPUT, mais je ne sais pas comment faire cela avec Spark SQL.

Pour concret:

var input = sqlContext.createDataFrame(Seq(
        (10L, "Joe Doe", 34),
        (11L, "Jane Doe", 31),
        (12L, "Alice Jones", 25)
        )).toDF("id", "name", "age")

var output = sqlContext.createDataFrame(Seq(
        (0L, "Jack Smith", 41, "yes", 1459204800L),
        (1L, "Jane Jones", 22, "no", 1459294200L),
        (2L, "Alice Smith", 31, "", 1459595700L)
        )).toDF("id", "name", "age", "init", "ts")


scala> input.show()
+---+-----------+---+
| id|       name|age|
+---+-----------+---+
| 10|    Joe Doe| 34|
| 11|   Jane Doe| 31|
| 12|Alice Jones| 25|
+---+-----------+---+

scala> input.printSchema()
root
 |-- id: long (nullable = false)
 |-- name: string (nullable = true)
 |-- age: integer (nullable = false)


scala> output.show()
+---+-----------+---+----+----------+
| id|       name|age|init|        ts|
+---+-----------+---+----+----------+
|  0| Jack Smith| 41| yes|1459204800|
|  1| Jane Jones| 22|  no|1459294200|
|  2|Alice Smith| 31|    |1459595700|
+---+-----------+---+----+----------+

scala> output.printSchema()
root
 |-- id: long (nullable = false)
 |-- name: string (nullable = true)
 |-- age: integer (nullable = false)
 |-- init: string (nullable = true)
 |-- ts: long (nullable = false)

J'aimerais ajouter toutes les lignes de input à la fin de output. En même temps, j'aimerais définir la colonne output de init comme chaîne vide '' et la colonne ts comme horodatage actuel, par exemple. 1461883875L.

Toute aide serait appréciée.

5

Les étincelles DataFrames sont immuables, il est donc impossible d’ajouter/d’insérer des lignes. Au lieu de cela, vous pouvez simplement ajouter des colonnes manquantes et utiliser UNION ALL:

output.unionAll(input.select($"*", lit(""), current_timestamp.cast("long")))
16
zero323

J'ai rencontré un problème similaire avec votre question SQL:

Je voulais ajouter un cadre de données à une table Hive existante, qui est également plus grande (plus de colonnes). Pour garder votre exemple: output est ma table existante et input pourrait être le cadre de données. Ma solution utilise simplement SQL et, par souci d'exhaustivité, je souhaite la fournir:

import org.Apache.spark.sql.SaveMode

var input = spark.createDataFrame(Seq(
        (10L, "Joe Doe", 34),
        (11L, "Jane Doe", 31),
        (12L, "Alice Jones", 25)
        )).toDF("id", "name", "age")

//--> just for a running example: In my case the table already exists
var output = spark.createDataFrame(Seq(
        (0L, "Jack Smith", 41, "yes", 1459204800L),
        (1L, "Jane Jones", 22, "no", 1459294200L),
        (2L, "Alice Smith", 31, "", 1459595700L)
        )).toDF("id", "name", "age", "init", "ts")

output.write.mode(SaveMode.Overwrite).saveAsTable("appendTest");
//<--

input.createOrReplaceTempView("inputTable");

spark.sql("INSERT INTO TABLE appendTest SELECT id, name, age, null, null FROM inputTable");
val df = spark.sql("SELECT * FROM appendTest")
df.show()

qui produit:

+---+-----------+---+----+----------+
| id|       name|age|init|        ts|
+---+-----------+---+----+----------+
|  0| Jack Smith| 41| yes|1459204800|
|  1| Jane Jones| 22|  no|1459294200|
|  2|Alice Smith| 31|    |1459595700|
| 12|Alice Jones| 25|null|      null|
| 11|   Jane Doe| 31|null|      null|
| 10|    Joe Doe| 34|null|      null|
+---+-----------+---+----+----------+

Si vous pouvez avoir le problème, que vous ne savez pas combien de champs sont manquants, vous pouvez utiliser un diff comme

val missingFields = output.schema.toSet.diff(input.schema.toSet)

et ensuite (dans un mauvais pseudo-code)

val sqlQuery = "INSERT INTO TABLE appendTest SELECT " + commaSeparatedColumnNames + commaSeparatedNullsForEachMissingField + " FROM inputTable"

J'espère aider les gens avec des problèmes futurs comme ça!

P.S .: Dans votre cas particulier (horodatage actuel + champ vide pour init), vous pouvez même utiliser

spark.sql("INSERT INTO TABLE appendTest SELECT id, name, age, '' as init, current_timestamp as ts FROM inputTable");

qui se traduit par 

+---+-----------+---+----+----------+
| id|       name|age|init|        ts|
+---+-----------+---+----+----------+
|  0| Jack Smith| 41| yes|1459204800|
|  1| Jane Jones| 22|  no|1459294200|
|  2|Alice Smith| 31|    |1459595700|
| 12|Alice Jones| 25|    |1521128513|
| 11|   Jane Doe| 31|    |1521128513|
| 10|    Joe Doe| 34|    |1521128513|
+---+-----------+---+----+----------+
1
Fabian