通过单个ID列减去DataFrames - 重复列的行为有所不同

时间:2017-07-14 14:39:46

标签: apache-spark apache-spark-sql

我正在尝试比较具有相同模式的两个DataFrame(在Spark 1.6.0中,使用Scala)来确定更新表中的哪些行已添加(即,旧表中不存在)。

我需要通过ID 执行(即检查单个列,而不是整行,以查看新内容)。某些行可能在版本之间发生了变化,因为它们在两个版本中都具有相同的 id ,但其他列已更改 - 我不希望这些在输出中,所以我不能简单地减去这两个版本。

根据各种建议,我在所选ID列上进行左外连接,然后从连接右侧的列中选择带空值的行(表示它们不存在于旧版本的表中):

def diffBy(field:String, newer:DataFrame, older:DataFrame): DataFrame = {
  newer.join(older, newer(field) === older(field), "left_outer")
       .select(older(field).isNull)
       // TODO just select the leftmost columns, removing the nulls
}

然而,这不起作用。 (第3行仅存在于较新版本中,因此应输出):

scala> newer.show
+---+-------+
| id|  value|
+---+-------+
|  3|  three|
|  2|two-new|
+---+-------+

scala> older.show
+---+-------+
| id|  value|
+---+-------+
|  1|    one|
|  2|two-old|
+---+-------+

scala> diffBy("id", newer, older).show
+---+-----+---+-----+
| id|value| id|value|
+---+-----+---+-----+
+---+-----+---+-----+

联接按预期工作:

scala> val joined = newer.join(older, newer("id") === older("id"), "left_outer")
scala> joined.show
+---+-------+----+-------+
| id|  value|  id|  value|
+---+-------+----+-------+
|  2|two-new|   2|two-old|
|  3|  three|null|   null|
+---+-------+----+-------+

所以问题在于选择用于过滤的列。

joined.where(older("id").isNull).show
+---+-----+---+-----+
| id|value| id|value|
+---+-----+---+-----+
+---+-----+---+-----+

也许是因为联接中的重复id列名称?但是,如果我使用value列(也是重复的)来检测空值,它会按预期工作:

joined.where(older("value").isNull).show
+---+-----+----+-----+
| id|value|  id|value|
+---+-----+----+-----+
|  3|three|null| null|
+---+-----+----+-----+

这里发生了什么 - 为什么idvalue的行为有所不同?

2 个答案:

答案 0 :(得分:1)

您可以使用名为“ leftanti”的特殊火花联接解决问题。它等于减号(在Oracle PL SQL中)。

val joined = newer.join(older, newer("id") === older("id"), "leftanti")

这只会从newer中选择列。

答案 1 :(得分:0)

我找到了解决问题的方法,但不解释为什么会出现问题。

似乎有必要创建一个别名,以便明确地引用最右边的id列,然后使用文本WHERE子句,以便我可以替换来自的合格列名称变量field

newer.join(older.as("o"), newer(field) === older(field), "left_outer")
     .where(s"o.$field IS NULL")