it-swarm.com.ru

Spark SQL: как добавить новую строку в таблицу данных (из другой таблицы)

Я использую Spark SQL с фреймами данных. У меня есть входной фрейм данных, и я хотел бы добавить (или вставить) его строки в больший фрейм данных, который имеет больше столбцов. Как бы я это сделал? 

Если бы это был SQL, я бы использовал INSERT INTO OUTPUT SELECT ... FROM INPUT, но я не знаю, как это сделать с Spark SQL.

Для конкретности:

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)

Я хотел бы добавить все строки input в конец output. В то же время я хотел бы установить для столбца outputinit пустую строку '', а для столбца ts текущую метку времени, например, 1461883875L.

Любая помощь будет оценена.

5
stackoverflowuser2010

Spark DataFrames являются неизменными, поэтому невозможно добавлять/вставлять строки. Вместо этого вы можете просто добавить отсутствующие столбцы и использовать UNION ALL:

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

У меня была похожая проблема, соответствующая вашему SQL-вопросу:

Я хотел добавить фрейм данных в существующую таблицу Hive, которая также больше (больше столбцов). Для примера: output - это моя существующая таблица, а input - это фрейм данных. Мое решение использует просто SQL, и для полноты картины я хочу его предоставить:

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()

какие выводы:

+---+-----------+---+----+----------+
| 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|
+---+-----------+---+----+----------+

Если у вас может быть проблема, что вы не знаете, сколько полей пропущено, вы можете использовать diff

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

а затем (в плохом псевдокоде)

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

Надеюсь помочь людям с такими проблемами в будущем!

P.S .: В вашем особом случае (текущая временная метка + пустое поле для init) вы можете даже использовать

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

что приводит к 

+---+-----------+---+----+----------+
| 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