如何将OR约束与多个半联接一起使用?

时间:2018-09-18 15:32:48

标签: apache-spark apache-spark-sql

如何在SparkSQL或Dataframe API中使用半联接实现以下SQL?

select * 
from foo
left join a on foo.id = a.id
left join b on foo.id = b.id
where exists (select 1 from a x where foo.id = x.id)
or exists (select 1 from b x where foo.id = x.id)
;

我已经尝试过了,但是它的作用是AND

.join(loincDF, foo("id") <=> a("id"), "leftsemi")
.join(loincDF, foo("id") <=> b("id"), "leftsemi")

2 个答案:

答案 0 :(得分:1)

您可以尝试以下操作:

foo.join(a, Seq("id"), "leftsemi")
   .union( foo.join(b, Seq("id"), "leftsemi") )
   .distinct

答案 1 :(得分:0)

正如@Elmar Macek的解决方案中指出的那样,left_semi的并集模仿OR子句的exists条件。但是,left_semi连接不会从right DataFrame中产生相应的列。要生成与涉及left join的SQL等效的结果,您可以先执行left_outer连接,然后再应用模仿exists函数的过滤逻辑。

以下是几种方法:

方法1:先执行left_outer次加入,然后进行left_semi次加入

val foo = Seq(
  (1, "f1"), (2, "f2"), (3, "f3"), (4, "f4"), (5, "f5")
).toDF("id", "name")

val a = Seq(
  (1, "a1"), (3, "a3")
).toDF("id", "desc")

val b = Seq(
  (1, "b1"), (4, "b4")
).toDF("id", "desc")

val bar = foo.
  join(a, Seq("id"), "left_outer").
  join(b, Seq("id"), "left_outer")

bar.join(a, Seq("id"), "left_semi").
  union(
    bar.join(b, Seq("id"), "left_semi")
  ).
  distinct.
  show
// +---+----+----+----+
// | id|name|desc|desc|
// +---+----+----+----+
// |  4|  f4|null|  b4|
// |  1|  f1|  a1|  b1|
// |  3|  f3|  a3|null|
// +---+----+----+----+

方法2:执行left_outer连接,然后null检查非关键列

val aNonKeyCols = a.columns.filter(_ != "id")
val a2 = a.withColumn("aCols", struct(aNonKeyCols.map(col): _*)).
  select("id", "aCols")

val bNonKeyCols = b.columns.filter(_ != "id")
val b2 = b.withColumn("bCols", struct(aNonKeyCols.map(col): _*)).
  select("id", "bCols")

val bar = foo.
  join(a2, Seq("id"), "left_outer").
  join(b2, Seq("id"), "left_outer").
  where($"aCols".isNotNull || $"bCols".isNotNull).
  select("id", "name", "aCols.*", "bCols.*")

请注意,假设存在许多列,ab的非关键列分别放置在struct中,以简化{{ 1}}检查。