记一次SparkSql的union操作异常

简介: 记一次SparkSql的union操作异常
在某次使用sparksql中的union合并两个DataFrame时,发现总是报类型不匹配的错误,但是检查后发现两个DataFrame中无论是列名和列的类型,都是完全相同的,下面复现一下这个错误
object SqlTest {
  def main(args: Array[String]): Unit = {
    // 设置日志输出的级别
    Logger.getLogger("org").setLevel(Level.ERROR)
    //初始化编程入口
    val session:SparkSession = SparkSession.builder.appName("name").master("local[2]").getOrCreate()
    import session.implicits._
    //创建第一个DataFrame
    var df1 = List[(String,String,Integer)](
      ("李奇峰","2019-5-12",88),
      ("李奇峰","2019-5-12",81),
      ("李奇峰","2019-5-12",82),
      ("李奇峰","2019-5-12",86)
    ).toDF("name","date","grade")
    println("df1的显示结果:")
    df1.show()
    //创建第二个DataFrame
    var df2 = List[(String,Integer,String)](
      ("李晓峰",88,"2019-5-12"),
      ("李晓峰",81,"2019-5-12"),
      ("李晓峰",82,"2019-5-12"),
      ("李晓峰",86,"2019-5-12")
    ).toDF("name","grade","date")
    println("df2的显示结果:")
    df2.show()
    //将两个DataFrame的date列转换为时间类型
    df1 = df1.withColumn("date",$"date".cast(DateType))
    df2 = df2.withColumn("date",$"date".cast(DateType))
    //进行合并操作
    val result = df1.union(df2)
    println("合并后的显示结果:")
    result.show()
  }
}

执行以上方法会存在一下报错

df1的显示结果:
+------+---------+-----+
|  name|     date|grade|
+------+---------+-----+
|李奇峰|2019-5-12|   88|
|李奇峰|2019-5-12|   81|
|李奇峰|2019-5-12|   82|
|李奇峰|2019-5-12|   86|
+------+---------+-----+

df2的显示结果:
+------+-----+---------+
|  name|grade|     date|
+------+-----+---------+
|李晓峰|   88|2019-5-12|
|李晓峰|   81|2019-5-12|
|李晓峰|   82|2019-5-12|
|李晓峰|   86|2019-5-12|
+------+-----+---------+

Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Exception in thread "main" org.apache.spark.sql.AnalysisException: Union can only be performed on tables with the compatible column types. int <> date at the second column of the second table;;
'Union下·
:- Project [name#7, cast(date#8 as date) AS date#26, grade#9]
:  +- Project [_1#3 AS name#7, _2#4 AS date#8, _3#5 AS grade#9]
:     +- LocalRelation [_1#3, _2#4, _3#5]
+- Project [name#20, course#21, cast(date#22 as date) AS date#30]
   +- Project [_1#16 AS name#20, _2#17 AS course#21, _3#18 AS date#22]
      +- LocalRelation [_1#16, _2#17, _3#18]

    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$class.failAnalysis(CheckAnalysis.scala:42)
    at org.apache.spark.sql.catalyst.analysis.Analyzer.failAnalysis(Analyzer.scala:95)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1$$anonfun$apply$12$$anonfun$apply$13.apply(CheckAnalysis.scala:293)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1$$anonfun$apply$12$$anonfun$apply$13.apply(CheckAnalysis.scala:290)
    at scala.collection.immutable.List.foreach(List.scala:392)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1$$anonfun$apply$12.apply(CheckAnalysis.scala:290)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1$$anonfun$apply$12.apply(CheckAnalysis.scala:279)
    at scala.collection.immutable.List.foreach(List.scala:392)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1.apply(CheckAnalysis.scala:279)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$$anonfun$checkAnalysis$1.apply(CheckAnalysis.scala:85)
    at org.apache.spark.sql.catalyst.trees.TreeNode.foreachUp(TreeNode.scala:127)
    at org.apache.spark.sql.catalyst.analysis.CheckAnalysis$class.checkAnalysis(CheckAnalysis.scala:85)
    at org.apache.spark.sql.catalyst.analysis.Analyzer.checkAnalysis(Analyzer.scala:95)
    at org.apache.spark.sql.catalyst.analysis.Analyzer$$anonfun$executeAndCheck$1.apply(Analyzer.scala:108)
    at org.apache.spark.sql.catalyst.analysis.Analyzer$$anonfun$executeAndCheck$1.apply(Analyzer.scala:105)
    at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper$.markInAnalyzer(AnalysisHelper.scala:201)
    at org.apache.spark.sql.catalyst.analysis.Analyzer.executeAndCheck(Analyzer.scala:105)
    at org.apache.spark.sql.execution.QueryExecution.analyzed$lzycompute(QueryExecution.scala:57)
    at org.apache.spark.sql.execution.QueryExecution.analyzed(QueryExecution.scala:55)
    at org.apache.spark.sql.execution.QueryExecution.assertAnalyzed(QueryExecution.scala:47)
    at org.apache.spark.sql.Dataset$.ofRows(Dataset.scala:79)
    at org.apache.spark.sql.Dataset.withSetOperator(Dataset.scala:3419)
    at org.apache.spark.sql.Dataset.union(Dataset.scala:1857)
    at SqlTest$.main(SqlTest.scala:34)
    at SqlTest.main(SqlTest.scala)

从报错可以看出是两个DataFrame中存在类型不兼容的问题,可是经过检查可以看到两个DataFrame中类型与列名都是完全相同的,无奈去查找Spark官网的Union,发现两个细节:

1. union操作和集合的并集并不等价,它不会去除重复数据。

2. union函数并不是按照列名合并,而是按照位置合并。即DataFrame的列名可以不相同,但对应位置的列将合并在一起。

根据以上第二个细节去检查代码,可以看出虽然两个DataFrame的列名和列对应的类型虽然都是相同的,但是他们之间的位置并不是相同的,所以要修改其中一个DataFrame的列的位置
修改后的代码如下:

object SqlTest {
  def main(args: Array[String]): Unit = {
    // 设置日志输出的级别
    Logger.getLogger("org").setLevel(Level.ERROR)
    //初始化编程入口
    val session:SparkSession = SparkSession.builder.appName("name").master("local[2]").getOrCreate()
    import session.implicits._
    //创建第一个DataFrame
    var df1 = List[(String,String,Integer)](
      ("李奇峰","2019-5-12",88),
      ("李奇峰","2019-5-12",81),
      ("李奇峰","2019-5-12",82),
      ("李奇峰","2019-5-12",86)
    ).toDF("name","date","grade")
    //创建第二个DataFrame
    var df2 = List[(String,Integer,String)](
      ("李晓峰",88,"2019-5-12"),
      ("李晓峰",81,"2019-5-12"),
      ("李晓峰",82,"2019-5-12"),
      ("李晓峰",86,"2019-5-12")
    ).toDF("name","grade","date")
    //将两个DataFrame的date列转换为时间类型
    df1 = df1.withColumn("date",$"date".cast(DateType))
    df2 = df2.withColumn("date",$"date".cast(DateType))
    //修改df2的列的位置
    df2 = df2.select("name","date","grade")
    //进行合并操作
    df1.union(df2).show()
  }
}

可以看出上述代码比之前的代码只多了一行调整列的位置的代码
df2 = df2.select("name","date","grade")
结果如下:

df1的显示结果:
+------+---------+-----+
|  name|     date|grade|
+------+---------+-----+
|李奇峰|2019-5-12|   88|
|李奇峰|2019-5-12|   81|
|李奇峰|2019-5-12|   82|
|李奇峰|2019-5-12|   86|
+------+---------+-----+

df2的显示结果:
+------+-----+---------+
|  name|grade|     date|
+------+-----+---------+
|李晓峰|   88|2019-5-12|
|李晓峰|   81|2019-5-12|
|李晓峰|   82|2019-5-12|
|李晓峰|   86|2019-5-12|
+------+-----+---------+

合并后的显示结果:
+------+----------+-----+
|  name|      date|grade|
+------+----------+-----+
|李奇峰|2019-05-12|   88|
|李奇峰|2019-05-12|   81|
|李奇峰|2019-05-12|   82|
|李奇峰|2019-05-12|   86|
|李晓峰|2019-05-12|   88|
|李晓峰|2019-05-12|   81|
|李晓峰|2019-05-12|   82|
|李晓峰|2019-05-12|   86|
+------+----------+-----+
目录
相关文章
|
机器学习/深度学习 监控 Python
tensorflow2.x多层感知机模型参数量和计算量的统计
tensorflow2.x多层感知机模型参数量和计算量的统计
367 0
|
Linux 开发工具 Python
CentOS7安装python3超详细教程
CentOS7安装python3超详细教程
1582 0
|
JSON Linux 网络安全
一文搞定:whois数据库查询域名信息(WHOIS)
一文搞定:whois数据库查询域名信息(WHOIS)
3727 0
一文搞定:whois数据库查询域名信息(WHOIS)
完美解决->“pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。”
完美解决->“pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。”
完美解决->“pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。”
|
关系型数据库 数据安全/隐私保护
Kerberos常见报错汇总
汇总了Kerberos在配置和使用过程中可能遇到的常见错误,包括密码不匹配、配置文件参数缺失、数据库文件不存在、日志文件路径错误等问题,并为每个问题提供了详细的错误复现、原因分析以及解决方案。
672 3
|
SQL 存储 Oracle
【赵渝强老师】Hive的分区表
Hive的分区表与Oracle、MySQL类似,通过分区条件将数据分隔存储,提高查询效率。本文介绍了静态分区表和动态分区表的创建与使用方法,包括具体SQL语句和执行计划分析,附带视频讲解。静态分区表需显式指定分区条件,而动态分区表则根据插入数据自动创建分区。
1075 1
如何做好技术选型
在软件开发领域,几乎每天都有新的技术框架诞生、更新,一些新的概念更是层出不穷,技术选型时,难免让人无从抉择。对于技术选型,我个人有以下几点建议。
1985 0
|
SQL 存储 关系型数据库
MySQL in和exists的取舍
本文讨论了SQL查询中IN和EXISTS的使用场景及其区别。IN适用于外表大而内表小的情况,会将子查询结果存储在临时表中;EXISTS则以外表为驱动表,适合外表小而内表大的情况,且不生成临时表。结论是:当子查询数据量大时,应使用EXISTS。
232 8
|
移动开发 安全 Swift
TIOBE 6月榜单:Swift强势挺进,编程语言版图的悄然变革
【6月更文挑战第21天】**TIOBE 6月榜:Swift晋升至第12,凸显其在苹果生态和移动开发中的重要性。自2014年发布以来,Swift凭借强类型、内存安全等特性赢得开发者青睐。排名上升源于苹果支持、开源跨平台、教育普及及性能提升。Swift的崛起影响行业生态,提升开发效率,预示着语言生态、跨平台和教育先行的趋势。未来,Swift有望扩展到更多领域,持续优化并深化教育影响。**
430 6
|
Python
Python中的贝叶斯分类器以及如何使用Sklearn库实现它。
【4月更文挑战第23天】Sklearn库支持多种贝叶斯分类器,如高斯朴素贝叶斯、多项式朴素贝叶斯和伯努利朴素贝叶斯。以下是一个使用GaussianNB的简要示例:导入必要库,加载鸢尾花数据集,将其划分为训练集和测试集,创建高斯朴素贝叶斯分类器,训练模型,预测并评估(通过准确率)模型性能。
226 0