为什么加入两个数据集并将其保存到文本文件会因NullPointerException而失败?

时间:2017-06-23 16:38:41

标签: scala apache-spark apache-spark-sql

我正在使用sqlContext数据框/ scala并且已经成功地写出了一些这样的数据。我现在需要对另一个表进行正确的连接,将其过滤到我感兴趣的数据。但是,当我尝试编写这个连接表时,它会导致NullPointerException。

这有效:

data
    .select($"id", $"text")
    .map(x => (x.getString(0), x.getString(1)).productIterator.mkString("\t"))
    .saveAsTextFile("/hdfs/filepath/output.tsv")

但这不是:

data
    .join(data2, Seq("id"), "right")
    .select($"id", $"text")
    .map(x => (x.getString(0), x.getString(1)).productIterator.mkString("\t"))
    .saveAsTextFile("/hdfs/filepath/output.tsv")

我得到的堆栈跟踪是:

Caused by: java.lang.NullPointerException
at $iwC$$iwC$$iwC$$iwC$$iwC$$$$3d99ae6e19b65c7f617b22f29b431fb$$$$$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$anonfun$1.apply(<console>:150)
at $iwC$$iwC$$iwC$$iwC$$iwC$$$$3d99ae6e19b65c7f617b22f29b431fb$$$$$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$anonfun$1.apply(<console>:149)
at scala.collection.Iterator$$anon$11.next(Iterator.scala:328)
at scala.collection.Iterator$$anon$11.next(Iterator.scala:328)
at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1$$anonfun$13$$anonfun$apply$6.apply$mcV$sp(PairRDDFunctions.scala:1198)
at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1$$anonfun$13$$anonfun$apply$6.apply(PairRDDFunctions.scala:1197)
at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1$$anonfun$13$$anonfun$apply$6.apply(PairRDDFunctions.scala:1197)
at org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1250)
at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1$$anonfun$13.apply(PairRDDFunctions.scala:1205)
at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1$$anonfun$13.apply(PairRDDFunctions.scala:1185)
at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)
at org.apache.spark.scheduler.Task.run(Task.scala:89)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:213)
... 3 more

同时运行这两个......

data
    .select($"id", $"text")
    .printSchema
data
    .join(data2, Seq("id"), "right")
    .select($"id", $"text")
    .printSchema

..产生相同的架构:

root
 |-- id: string (nullable = true)
 |-- text: string (nullable = true)

如果我添加.show(5)表格看起来相同(除了加入的表格只包含我感兴趣的数据)

+-------+--------+
|   id  |    text|
+-------+--------+
|   1   |some... |
|   2   |text... |
|   3   |here... |
|   4   |foo...  |
|   5   |bar...  |
+-------+--------+
only showing top 5 rows
+-------+--------+
|   id  |    text|
+-------+--------+
|   1   |some... |
|   4   |foo...  |
|   5   |bar...  |
|   7   |other...|
|   9   |stuff...|
+-------+--------+
only showing top 5 rows

我还尝试了连接到另一个表:data.join(data3, Seq("id"), "right")并获得相同的NullPointerException错误。为什么呢?

2 个答案:

答案 0 :(得分:0)

(这可能不是确定的答案,但不能用评论来传达这个想法)

  

但这不是

重点是你创建一个元组只是为了在之后立即对其进行解构以获得可能不存在的值。

我用以下内容重写您的代码:

data.
  join(data2, Seq("id"), "right").
  select($"id", $"text").
  as[(String, String)].  // <-- Added explicit type conversion
  map { case (id, text) => s"$id\t$text" }.
  write.
  text("/hdfs/filepath/output.tsv")

由于您只是连接两个字符串字段并将其保存到文本文件中,因此应该无异常地传递。

答案 1 :(得分:0)

感谢来自@RameshMaharjan的提示,我意识到在加入数据和数据2之后有3个空值。如果你有空值,那么你会得到一个空指针异常......:)

无论如何,我发现有2个选项需要修复:

1:内连接而不是外连接

data
    .join(data2, Seq("id"), "right")
    .where($"id".isNotNull)
    .select($"id", $"text")
    .map(x => (x.getString(0), x.getString(1)).productIterator.mkString("\t"))
    .saveAsTextFile("/hdfs/filepath/output.tsv")

2:摆脱空值

{{1}}