spark-sql

本文展示了如何使用SparkDataFrame进行Excel文件的读写操作,包括设置schema和处理选项。同时,文章详细说明了如何与Elasticsearch(ES)进行数据交互,包括读取和写入索引,并处理日期格式和空值异常。此外,还涵盖了DataFrame中的数据类型转换以及如何处理JSON字段和数据去重问题。
摘要由CSDN通过智能技术生成

spark dataframe 遇到的问题

  maven jar
	<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <scala.version>2.12</scala.version>
        <spark.version>3.0.0</spark.version>
        <es.version>7.11.1</es.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
         <!--spark-core 依赖-->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_${scala.version}</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <!--spark-sql 依赖-->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_${scala.version}</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <!--spark-es 依赖-->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch-spark-30_${scala.version}</artifactId>
            <version>7.12.1</version>
        </dependency>
		<!--spark-excel 依赖-->
        <dependency>
            <groupId>com.crealytics</groupId>
            <artifactId>spark-excel_${scala.version}</artifactId>
            <version>3.0.3_0.18.7</version>
        </dependency>
        <!--雪花算法id 依赖-->
   		<dependency>
            <groupId>cn.ipokerface</groupId>
            <artifactId>snowflake-id-generator</artifactId>
            <version>2.5.0</version>
        </dependency>
    </dependencies>

1、spark 读写取 excel文件

	
  val schema = new StructType(Array(
    StructField("id",DataTypes.StringType),
    StructField("name",DataTypes.StringType),
    StructField("addr",DataTypes.StringType)
    }
    
  def main(args: Array[String]): Unit = {
    val spark = SparkSession
      .builder()
      .master("local[*]")
      .appName("xxx")
   //   .config("es.index.auto.create",true)
      .getOrCreate()
// ------------------读取excel---------------------------
    val excelReadDF = spark.read
      .option("header", "true")         //是否带有表头
      .option("inferSchema", "true")    // 自行推断表头
      .option("dataAddress", "0!A1")    //  "0!A1"   0 代表第一个sheet 页  !A1 从A1单元格读取
//      .option("encoding","GBK")       //  处理 csv 时中文乱码
      .schema(schema)                   //  固定表头
//      .format("csv")                  //  文件格式 csv
      .format("com.crealytics.spark.excel")     //excel 文件格式
      .load("G:\\data\\读文件.xlsx")       //本地文件地址
 
// ------------------写excel---------------------------
    val savefilePath = "G:\\data\\写文件.xlsx";
    excelReadDF.write
      .format("com.crealytics.spark.excel")
      .option("dataAddress", "0!A1")
      .option("useHeader", "false")
      .option("header", "true")
      .mode(SaveMode.Overwrite)
      .save(savefilePath)
  }

2、读写es

  def main(args: Array[String]): Unit = {
   
    val spark: SparkSession = SparkSession.builder.appName("readEs").master("local[*]").getOrCreate()

    val options: Map[String, String] = Map("es.nodes" -> "ip",
      "es.port" -> "9200", "pushdown" -> "true",
      //spark和es非同一网段时增加该配置
      "es.nodes.wan.only" -> "true",
      "es.mapping.date.rich" -> "false", //处理es 日期转换格式问题
      "es.field.read.empty.as.null" -> "false", // scala.None$ is not a valid external type for schema of string
      //并发更新时, 如果更新在同一条记录则会冲突,增加该配置
      "es.update.retry.on.conflict" -> "3",
      //决定spark会生成多少个partition对应执行的task
      "es.input.max.docs.per.partition" -> "5000")
// ------------------读取es---------------------------

     // 1、直接读取成df,这种方式不支持复杂查询,如果需要复杂查询请采用 方式2 读取成rdd
    val inputDF: DataFrame = spark.read.format("org.elasticsearch.spark.sql")
      .options(options).load("es-index-name")  //es 索引名称
      .where("pubtime>'2023-05-01 00:00:00'")  // 条件
      .select("nameZh", "title", "content", "pubtime") // 查询字段
      .orderBy(desc("id"),asc("pubtime"))  //字段排序 全局排序
    inputDF.printSchema()
     //2、 读取成rdd
     val rdd = spark.sparkContext.esJsonRDD("es-index-name",
      """
        |{
        |  "query": {
        |    "match": {"_id": "xxxxxxxxxx"}
        |  }
        |}
        |""".stripMargin)
    rdd.foreach(println)
// ------------------写入es---------------------------
    inputDF.write.format("org.elasticsearch.spark.sql")
      .options(options).mode(SaveMode.Append)
      .save("es-index-name")

3、 dataframe 中转化使用java 对象

public class SchoolJava implements Serializable {
    private String id;
    private String name;
    private String addr;
    //省略 get set 
 import spark.implicits._     //隐式转换
 val userDS: Dataset[SchoolJava] = inputDF.as(Encoders.bean(classOf[org.example.dto.SchoolJava]))

4、 dataframe 中转化使用scala 对象

case class SchoolScala(id: String, name: String, addr: String)
 import spark.implicits._
 val userDS: Dataset[SchoolScala] = inputDF.as[SchoolScala]

4、es 日期转换异常和空值异常 scala.None$ is not a valid external type for schema of string
“es.mapping.date.rich” -> “false”,
“es.field.read.empty.as.null” -> “false”,

5、df 里新增列
1)直接调用withColumn() 函数,但是 添加的列 只能是 依赖于已经存在的列 或者 添加常值列(这一列的值都一样)

2) 增加列 非依赖于已存在的列 或者 常值列的数值不一致的
这种情况下 就需要 先将dataframe 转成rdd 在rdd中添加列然后再转成dataframe,其中可能会遇到问题 1-df转成rdd后schema 丢失变成RDD[ROW],在对RDD[ROW] 无法调用 toDF() 将rdd 转化成df了。可以调用 createDataFrame()函数,生成新的DF。具体如下
3)可以直接在sparksql 中直接根据拼接
… createTempView(“tt”) //先创建临时表 通过 concat_ws函数 将不同的列 以 :拼接,不带分隔符用concat()函数

val frame: DataFrame = spark.sql("select *, concat(column10,column11) as column15  from tt ")
val frame: DataFrame = spark.sql("select *, concat_ws(\":\", column10, column11,column12) as column15 from tt ")

package org.example

import cn.ipokerface.snowflake.SnowflakeIdGenerator
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types._
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

import scala.collection.mutable

object DataDeal {
  val generator = new SnowflakeIdGenerator(1, 1)
  def main(args: Array[String]): Unit = {
    System.setProperty("hadoop.home.dir", "D:\\spark-hadoop-win")
    val spark: SparkSession = SparkSession.builder.appName("DataDeal").master("local[*]").getOrCreate()
    val options: Map[String, String] = Map("es.nodes" -> "xxxx",
      "es.port" -> "9200", "pushdown" -> "true",
      //spark和es非同一网段时增加该配置
      "es.nodes.wan.only" -> "true",
      "es.mapping.date.rich" -> "false", //处理es 日期转换格式问题
      "es.field.read.empty.as.null" -> "false", //scala.None$ is not a valid external type for schema of string
      //并发更新时, 如果更新在同一条记录则会冲突,增加该配置
      "es.update.retry.on.conflict" -> "3",
      //决定spark会生成多少个partition对应执行的task
      "es.input.max.docs.per.partition" -> "5000")
      // 隐式转换
    import spark.implicits._  
    // 注册udf函数
    spark.udf.register("getNewId", getNewId _)
    //直接从es 读取成df
    val inputDF: DataFrame = spark.read.format("org.elasticsearch.spark.sql")
      .options(options).load("es-index")
      .where("pubtime>'2023-05-10 00:00:00'")
      .select("id", "name_zh", "sub_content", "json_content")
    inputDF.printSchema()
    inputDF.show()
    //使用get_json_object 进行json 中字段提取生成msg字段
    val jsonobj: DataFrame = inputDF.select(get_json_object($"json_content", "$.msg").as("msg")
    )
    jsonobj.printSchema()
    jsonobj.show()
    // 设置字段类型
    val fieldsConf = new mutable.HashMap[String, DataType]()
    fieldsConf += (("new_id_str", StringType))
    //将df 转换成rdd, 丢失类型转化成Row 类型,动态添加字段到rdd[Row] 中
    val dataSF: RDD[Row] = jsonobj.rdd.map(
      row => {
        val buffer = Row.unapplySeq(row).get.toBuffer
        buffer.append("" + getNewId())
        var schemaNew: StructType = row.schema
        fieldsConf.foreach(conf => {
          schemaNew = schemaNew.add(conf._1, conf._2)
        })

        // 使用Row的子类GenericRowWithSchema创建新的Row
        val newSchema = new GenericRowWithSchema(buffer.toArray, schemaNew).asInstanceOf[Row]
        newSchema
      })
    dataSF.foreach(row => println(row))
    dataSF.repartition(2)
    //设置shema 
    var schema: StructType = StructType(List
    (
      StructField("msg", StringType, false),
      StructField("new_id_str", StringType, false)
    ))
	// 根据RDD[Row] 和shema 生成新的df 
    var newDF: DataFrame = spark.createDataFrame(dataSF, schema)
    newDF.printSchema()
    newDF.show()
  }

  // 雪花算法生成new_id 
  def getNewId(): String = {
    generator.nextId() + ""
  }

6、df里获取字段值是json 的处理
可以直接使用 get_json_object 函数,参考 5 中代码
7、df 里去重 可以采用distinct() 或者 dropDuplicates()

distinct() 所有字段值完全相同的行
dropDuplicates() 指定某个或多个字段相同 去重处理

8、 大小写不敏感导致字段重名
apache.spark.sql.AnalysisException: Found duplicate column(s) in the data schema: xxx;

 sparksession.sql("set spark.sql.caseSensitive=true")  //设置大小写敏感
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值