为什么array_contains在SQL中接受两个参数的列而不在Dataset API中接受列?

时间:2018-05-18 13:56:14

标签: apache-spark apache-spark-sql

我一直在回顾有关StackOverflow上array_contains(和isin)方法的问题和答案,但我仍然无法回答以下问题:

为什么SQL中的array_contains接受列(引用)作为参数,而标准函数却没有?

我可以理解上述问题很容易被标记为"主要是基于意见的"或者类似的,让我把它改为:

如何使用array_contains标准函数,以便它接受来自列的参数(值)?

scala> spark.version
res0: String = 2.3.0

val codes = Seq(
  (Seq(1, 2, 3), 2),
  (Seq(1), 1),
  (Seq.empty[Int], 1),
  (Seq(2, 4, 6), 0)).toDF("codes", "cd")
scala> codes.show
+---------+---+
|    codes| cd|
+---------+---+
|[1, 2, 3]|  2|
|      [1]|  1|
|       []|  1|
|[2, 4, 6]|  0|
+---------+---+

// array_contains in SQL mode works with arguments being columns
val q = codes.where("array_contains(codes, cd)")
scala> q.show
+---------+---+
|    codes| cd|
+---------+---+
|[1, 2, 3]|  2|
|      [1]|  1|
+---------+---+

// array_contains standard function with Columns does NOT work. Why?!
// How to change it so it would work (without reverting to SQL expr)?
scala> val q = codes.where(array_contains($"codes", $"cd"))
java.lang.RuntimeException: Unsupported literal type class org.apache.spark.sql.ColumnName cd
  at org.apache.spark.sql.catalyst.expressions.Literal$.apply(literals.scala:77)
  at org.apache.spark.sql.functions$.array_contains(functions.scala:2988)
  ... 49 elided

2 个答案:

答案 0 :(得分:5)

仅仅因为没有人关心实施(Column, Column) => Column变种。如果您检查源代码,您将看到设计中没有任何内容,禁止您创建一个,因为标准工作流程是将非Column参数转换为文字。

它甚至没有特别的特色。还有其他函数没有包装器采用额外的Column参数,包括但不限于不同的日期/时间处理函数和数学函数。

答案 1 :(得分:4)

由于底层函数ArrayContains确实采用expr论证,你总是可以作弊。

scala> codes.where(new Column(ArrayContains($"codes".expr, $"cd".expr))).show
+---------+---+
|    codes| cd|
+---------+---+
|[1, 2, 3]|  2|
|      [1]|  1|
+---------+---+
**/

与user9812147一样,这里的问题只是SQL Parser能够直接访问ArrayContains函数。虽然看起来直接的函数调用强迫"值"部分被视为文字。