spark从入门到放弃二十八:Spark Sql (1)Data Set

本文介绍Spark SQL模块中的DataFrame和DataSet概念,演示如何通过SQLContext创建并操作数据集,涵盖查询、过滤及聚合等常见操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章地址:http://www.haha174.top/article/details/257834
项目源码:https://github.com/haha174/spark.git

1.简介

Spark Sql 是Spark 中的一个模块,主要是用于进行结构化数据处理。它提供的最核心的编程抽象,就是DataFrame。同时Spark Sql 还可以作为分布式sql 查询引擎。Saprk Sql 最重要的功能之一,就是从hive 中查询数据。

Data Frame 可以理解为 以列的形式组织的,分布式的数据集合。它其实和关系型数据库中的表非常类似,但是底层做了许多的优化。DataFrame可以通过很对源进行构建,包括结构化数据文件,hive中的表,外部的关系型数据库以及RDD.

Dataset是从Spark 1.6开始引入的一个新的抽象,当时还是处于alpha版本;然而在Spark 2.0,它已经变成了稳定版了.DataFrame是特殊的Dataset,它在编译时不会对模式进行检测

2.SQLContext

首先要使用spark sql 首先就得创建一个SQLContext 对象,或者是他的子类对象或者是HiveContext 的对象。
2.1 创建DataSet(这里没有使用SparkSession 的方式)
使用SQLContext,可以从RDD,hive 表或者其他的数据源,来创建一个DataSet,以下是JSON文件创建DataSet的例子:
SQLContext sqlContext=new SQLContext(sc);
DataSet df=sqlContext.read().json(“hdfs://spark1:9000/students.json”);
df.show()
下面给出一个java 示例:

public class DataFrameStudy {

    public static void main(String[] args) {
        SparkConf conf=new SparkConf().setAppName("DataFrameStudy");
        JavaSparkContext sc=new JavaSparkContext(conf);
        SQLContext sqlContext=new SQLContext(sc);
        DataFrameReader reader=sqlContext.read();
        Dataset ds= reader.json("hdfs://hadoop:8020/data/students.json");
        ds.show();
    }

}

students.json 长这个样子

{"id":1,"name":"java","age":18}
{"id":2,"name":"spark","age":19}
{"id":3,"name":"scala","age":20}

将打成的jar提交到spark 集群上面去运行下面给出submit 脚本

/apps/soft/spark-2.2.1-bin-hadoop2.7/bin/spark-submit \
 --class com.wen.saprk.sql.core.DataFrameStudy  \
--num-executors 3  \
--driver-memory 1g   \
 --executor-memory 1g  \  
--executor-cores 1  \
 --master spark://192.168.1.22:7077 /data/spark-submit/spl/dataframe/spark-study-sql-java-1.0-SNAPSHOT-jar-with-dependencies.jar

赋予执行权限

chmod  777 submit-sql.sh

执行即可看到
这里写图片描述

下面给出scala 示例

object DataFrameStudy {
  def main(args: Array[String]): Unit = {
      val conf=new SparkConf().setAppName("DataFrameStudy")
      val  sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
     val reader = sqlContext.read
      val ds = reader.json("hdfs://hadoop:8020/data/students.json")
      ds.show()
  }
}

结果和脚本和上面一样就不再演示了

3.HiveContext

除了基本的SQLContext 以外,还可以使用它的子类–HiveContext.HiveContext的功能除了包含SqlContext 提供的所有功能之外,还包含了额外的专门针对于Hive的一些功能。这些额外的功能包括:使用HiveQL 语法来编写和执行SQL,使用Hive中的UDF函数,从Hive表中读取数据。
要使用HiveContext,就必须预先安装好Hive,SQLContext支持的数据源,HiveContext也同样支持。对于Spark 1.3.x 以上的版本都推荐使用HiveContext,因为他的功能更加的丰富和完善。
Spark Sql 还支持用spark.sql.dialect 参数设置Sql的方言,使用SqlContext的setConf()既可以进行设置。对于SqlContext ,它支持sql 一种方言。对于HiveContext 默认的方言是hiveql.

4.Data Set 常用操作

下面给出java 示例:

public class DataFrameOperation {
    public static void main(String[] args) {
        SparkConf conf=new SparkConf().setAppName("DataFrameOperation");
        JavaSparkContext sc=new JavaSparkContext(conf);
        SQLContext sqlContext=new SQLContext(sc);
        DataFrameReader reader=sqlContext.read();
        //创建出dataset  可以理解为一张表
        Dataset ds= reader.json("hdfs://hadoop:8020/data/students.json");
        //打印dataFrame中的所有数据
        ds.show();
        ////打印dataFrame的元数据
        ds.printSchema();
        //查询某列所有的数据
        ds.select("name").show();
        //查询某几列所有的数据并对列进行计算
        ds.select(ds.col("name"),ds.col("age").plus(1)).show();
        //根据某一列的值进行过滤
        ds.filter(ds.col("age").gt("18")).show();
        //根据某一列进行分组聚合
        ds.groupBy(ds.col("age")).count().show();
        sc.close();
    }
}

提交脚本运行即可

/apps/soft/spark-2.2.1-bin-hadoop2.7/bin/spark-submit  --class com.wen.spark.sql.core.DataFrameOperation  --num-executors 1  --driver-memory 1g   --executor-memory 1g  --executor-cores 1   --master spark://192.168.1.22:7077 /data/spark-submit/spl/dataframe/java/spark-study-sql-java-1.0-SNAPSHOT-jar-with-dependencies.jar

下面给出scala 示例


  object DataFrameOperation {
    def main(args: Array[String]): Unit = {
      val conf = new SparkConf().setAppName("DataFrameOperation")
      val sc = new JavaSparkContext(conf)
      val sqlContext = new SQLContext(sc)
      val reader = sqlContext.read
      //创建出dataset  可以理解为一张表
      val ds = reader.json("hdfs://hadoop:8020/data/students.json")
      //打印dataFrame中的所有数据
      ds.show()
      ////打印dataFrame的元数据
      ds.printSchema()
      //查询某列所有的数据
      ds.select("name").show()
      //查询某几列所有的数据并对列进行计算
      ds.select(ds.col("name"), ds.col("age").plus(1)).show()
      //根据某一列的值进行过滤
      ds.filter(ds.col("age").gt("18")).show()
      //根据某一列进行分组聚合
      ds.groupBy(ds.col("age")).count.show()
      sc.close()
    }
}

欢迎关注,更多福利

这里写图片描述

<think>好的,我现在遇到了一个Spark SQL的错误,需要仔细分析解决。用户提到的错误信息是:“Exception in thread "main" org.apache.spark.sql.AnalysisException: Cannot write incompatible data to table 'unknown'”。首先,我需要理解这个错误的原因,然后找出解决方法。 首先,错误信息中提到无法向表'unknown'写入不兼容的数据。可能表名显示为'unknown'是因为Spark无法识别目标表的结构,或者表不存在。这时候需要考虑几个可能的原因: 1. **目标表不存在**:当尝试写入一个不存在的表时,Spark可能无法推断表结构,导致表名显示为'unknown'。这时候需要检查是否在写入之前创建了表,或者是否正确指定了表名。 2. **数据模式不匹配**:如果目标表已经存在,但DataFrame的schema与表的schema不一致,比如列名、数据类型、列的顺序或数量不同,就会导致不兼容的错误。 3. **保存模式(SaveMode)的问题**:使用`SaveMode.Append`时,如果数据模式与表结构不匹配,会抛出异常。而使用`SaveMode.Overwrite`可能会忽略模式差异,但也要看具体配置。 接下来,我需要逐步验证这些可能性。首先,检查用户代码中写入表的代码部分,确认表名是否正确,以及表是否存在。例如,使用`spark.sql("SHOW TABLES")`来查看表是否存在。如果表不存在,可能需要先创建表,或者在写入时指定模式创建,比如使用`df.write.option("path", "...").saveAsTable("tableName")`,并确保路径正确。 如果表存在,接下来比较DataFrame的schema和表的schema是否一致。可以使用`df.printSchema()`打印DataFrame的结构,然后与表的DDL对比。如果有列名不同、类型不兼容(如String转成Int),或者列数不一致,就需要调整DataFrame的结构,比如使用`select`或`withColumn`来重命名、转换类型或调整列顺序。 另外,保存模式也可能影响结果。如果使用`mode("append")`,必须保证DataFrame和表的schema完全兼容。而使用`mode("overwrite")`时,如果启用了`spark.sql.legacy.allowCreatingManagedTableUsingNonemptyLocation`(在旧版本中),可能会覆盖数据,但仍需注意模式兼容性。不过,通常建议在覆盖时也保持模式一致,或者明确指定模式覆盖选项,如`option("overwriteSchema", "true")`(如果支持的话)。 还有一个可能性是分区表的分区列不匹配。如果目标表是分区表,而DataFrame没有包含正确的分区列,或者分区列的数据类型不匹配,也会导致错误。需要检查分区列的定义,并在写入时指定正确的分区列,如使用`partitionBy("partition_col")`。 此外,表的存储格式(如Parquet、ORC等)也可能影响写入兼容性,但通常Spark会自动处理格式转换,除非有特殊配置。不过,如果表是Hive表,存储格式和SerDe设置可能需要特别关注。 最后,检查Spark的版本和相关配置。某些版本可能存在已知问题,更新版本或调整配置参数可能解决问题。例如,检查`spark.sql.hive.convertMetastoreParquet`等配置,确保其与数据写入方式兼容。 总结解决步骤: 1. 确认目标表是否存在,表名是否正确。 2. 对比DataFrame和表的schema,确保一致。 3. 调整保存模式或使用选项允许模式覆盖。 4. 检查分区列和存储格式设置。 5. 必要时重新创建表或调整DataFrame结构。</think>当遇到 `org.apache.spark.sql.AnalysisException: Cannot write incompatible data to table 'unknown'` 错误时,通常表示 **Spark SQL 尝试写入的数据与目标表的模式(Schema)不兼容**。以下是逐步排查和解决方法: --- ### 1. 确认目标表是否存在 **现象**:表名显示为 `'unknown'`,可能是由于目标表不存在或未正确引用。 **解决方法**: ```scala // 检查表是否存在(Hive表示例) spark.sql("SHOW TABLES LIKE 'your_table_name'").show() // 如果表不存在,先创建表(显式定义Schema) spark.sql(""" CREATE TABLE your_table_name ( id INT, name STRING, value DOUBLE ) USING PARQUET """) ``` --- ### 2. 检查Schema兼容性 **核心原因**:DataFrame的列名、数据类型、顺序或数量与目标表不匹配。 **操作步骤**: ```scala // 打印DataFrame的Schema df.printSchema() // 打印目标表的Schema spark.sql("DESCRIBE your_table_name").show() ``` - **列名不匹配**:重命名列。 ```scala val adjustedDF = df.withColumnRenamed("old_col", "new_col") ``` - **数据类型不兼容**:转换类型。 ```scala import org.apache.spark.sql.types.IntegerType val adjustedDF = df.withColumn("id", $"id".cast(IntegerType)) ``` - **列顺序/数量不一致**:显式选择列。 ```scala val adjustedDF = df.select("id", "name", "value") // 按目标表顺序选择 ``` --- ### 3. 调整保存模式(SaveMode) - **`Append` 模式**:要求DataFrame与目标表Schema完全兼容。 ```scala df.write.mode("append").saveAsTable("your_table_name") ``` - **`Overwrite` 模式**:允许覆盖数据,但需注意Schema兼容性。 ```scala df.write.mode("overwrite").saveAsTable("your_table_name") ``` - **强制覆盖Schema(谨慎使用)**: 某些Spark版本支持通过配置覆盖Schema: ```scala spark.conf.set("spark.sql.legacy.allowCreatingManagedTableUsingNonemptyLocation", "true") df.write.mode("overwrite").option("overwriteSchema", "true").saveAsTable("your_table_name") ``` --- ### 4. 分区表兼容性 如果目标表是分区表,需确保: - DataFrame包含所有分区列。 - 分区列的数据类型与表定义一致。 - 写入时指定分区列: ```scala df.write.partitionBy("date", "region").saveAsTable("partitioned_table") ``` --- ### 5. 重新创建表(最后一招) 如果Schema差异无法调和,可删除旧表并重新创建: ```scala spark.sql("DROP TABLE IF EXISTS your_table_name") df.write.saveAsTable("your_table_name") // 自动推断Schema创建表 ``` --- ### 总结 此错误的根本原因是 **数据与表的Schema不兼容**。解决思路为: 1. 确认表存在且名称正确。 2. 确保DataFrame与目标表的Schema完全一致。 3. 根据需求选择合适的保存模式(`append`/`overwrite`)。 4. 对分区表额外检查分区列定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值