在Spark中编写唯一值,同时保留旧值

时间:2019-01-28 09:52:06

标签: apache-spark dataframe apache-spark-sql

我有一个按计划执行的Spark作业。

当我将结果DataFrame写入数据目标(S3,HDFS,DB ...)时,我希望Spark写入的内容不会与特定列重复。

示例:

假设MY_ID是唯一列。

第一次执行:

--------------
|MY_ID|MY_VAL|
--------------
|  1  |   5  |
|  2  |   9  |
|  3  |   6  |
--------------

第二次执行:

--------------
|MY_ID|MY_VAL|
--------------
|  2  |   9  |
|  3  |   2  |
|  4  |   4  |
--------------

在两次执行之后,我希望在数据目标中找到的内容是这样的:

--------------
|MY_ID|MY_VAL|
--------------
|  1  |   5  |
|  2  |   9  |
|  3  |   6  |
|  4  |   4  |
--------------

预期输出是第一次执行的结果,并附加了第二次执行的结果。如果MY_ID的值已经存在,则保留旧值,丢弃新执行的结果(在这种情况下,第二个执行要为MY_ID 3写入MY_VAL 9。由于该记录从第一次执行起就已经存在,因此将删除新记录。)

因此,distinct()函数不足以保证这种情况。列MY_ID的唯一性即使在转储的输出中也应保持不变。

是否有任何解决方案可以合理的计算成本保证此属性? (与关系数据库中的UNIQUE基本相同。)

2 个答案:

答案 0 :(得分:0)

您可以在第一次和第二次迭代中执行fullOuterJoin

val joined = firstIteration.join(secondIteration, Seq("MY_ID"), "fullouter")

scala> joined.show
+-----+------+------+
|MY_ID|MY_VAL|MY_VAL|
+-----+------+------+
|    1|     5|  null|
|    3|     6|     2|
|    4|  null|     4|
|    2|     9|     9|
+-----+------+------+

从结果表中,如果firstIteration的MY_VAL有值,则可以按原样使用它。否则,如果它的null(表示密钥仅在第二次迭代中出现)。使用secondIteration的MY_VAL中的值。

scala> joined.withColumn("result", when(firstIteration.col("MY_VAL").isNull, secondIteration.col("MY_VAL"))
        .otherwise(firstIteration.col("MY_VAL")))
       .drop("MY_VAL")
       .show
+-----+------+
|MY_ID|result|
+-----+------+
|    1|     5|
|    3|     6|
|    4|     4|
|    2|     9|
+-----+------+

答案 1 :(得分:-1)

不确定使用的是Scala还是Python,但请看一下dropDuplicates函数,该函数允许您指定一个或多个列: https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.Dataset