增量清洗(超详细!!!)

文章介绍了如何使用SparkAPI进行数据清洗,结合Hive操作处理ods库数据,合并到dwd库,并展示了清洗过程的关键步骤。
摘要由CSDN通过智能技术生成

在这里插入图片描述

清洗要求

抽取ods库sku_info表中昨天的分区数据,并结合dim_sku_info最新分区现有的数据,根据id合并数据到dwd库中dim_sku_info的分区表(合并是指对dwd层数据进行插入或修改,需修改的数据以id为合并字段,根据create_time排序取最新的一条),分区字段为etl_date且值与ods库的相对应表该值相等,并添加dwd_insert_user、dwd_insert_time、dwd_modify_user、dwd_modify_time四列,其中dwd_insert_user、dwd_modify_user均填写“user1”。若该条数据第一次进入数仓dwd层则dwd_insert_time、dwd_modify_time均填写当前操作时间,并进行数据类型转换。若该数据在进入dwd层时发生了合并修改,则dwd_insert_time时间不变,dwd_modify_time存当前操作时间,其余列存最新的值。使用hive cli查询表zang_dim_sku_info的字段id、sku_desc、dwd_insert_user、dwd_modify_time、etl_date,条件为最新分区的数据,id大于等于15且小于等于20,并且按照id升序排序

清洗前置准备

写入SparkAPI

//spark API
    Logger.getLogger("org").setLevel(Level.OFF)
    System.setProperty("HADOOP_USER_NAME", "root")
     val sparkConf = new SparkConf()
      .setAppName("qingxi")
      .setMaster("local[*]")
    val sparkSession = SparkSession.builder()
      .config(sparkConf)
      .config("hive.metastore.uris", "thrift://bigdata1:9083")
      .config("dfs.client.use.datanode.hostname", "true")
      .config("spark.sql.parquet.writeLegacyFormat", "true")
      .enableHiveSupport()
      .getOrCreate()

查看dwd库中数据

写入dwd库中表数据代码

sparkSession.read.table("ods.sku_info")
       // 插入用户
        .withColumn("dwd_insert_user",lit("user1")) 
        //插入时间
        .withColumn("dwd_insert_time",lit(from_unixtime(unix_timestamp(),"yyyy-MM-dd HH:mm:ss")))
        //修改用户
        .withColumn("dwd_modify_user",lit("user1"))
        //修改时间
        .withColumn("dwd_modify_time",lit(from_unixtime(unix_timestamp(),"yyyy-MM-dd HH:mm:ss")))
        .write
        .mode(SaveMode.Overwrite) //以重写方式写入
        .partitionBy("etl_date")  //根据etl_date 分区
        .saveAsTable("dwd.dim_sku_info") //写入表名dim_sku_info
        //查看dwd库中清洗的表数据
        sparkSession.sql("select * from dwd.dim_sku_info").show()
        

图片展示
在这里插入图片描述

将未清洗的ods库中的数据清洗

val ods_sku_info = sparkSession.table("ods.sku_info")
      .withColumn("dwd_insert_user", lit("user1"))
      .withColumn("dwd_insert_time", lit(from_unixtime(unix_timestamp(), "yyyy-MM-dd HH:mm:ss")))
      .withColumn("dwd_modify_user", lit("user1"))
      .withColumn("dwd_modify_time", lit(from_unixtime(unix_timestamp(), "yyyy-MM-dd HH:mm:ss")))
  //查看sku_info 表中数据
    ods_sku_info.show()

清洗后数据展示

在这里插入图片描述

之前清洗过的dwd 表中数据用dwd_sku_info 存储

val dwd_sku_info = sparkSession.table("dwd.dim_sku_info")

将新旧表数据合并

//WindowSpec是Apache Spark中的一个类,它是一个窗口规范,用于定义分区、排序和窗口边界
val windowSpec = Window.partitionBy("id").orderBy(desc("create_time"))
//将两个表数据按照相同格式合并
    val union_table = ods_sku_info.select(dwd_sku_info.columns.map(col): _*).union(dwd_sku_info)
    //创建一个新的列“sordId”,是基于给定的窗口规范为每一行分配的行号。
      .withColumn("sordId", row_number().over(windowSpec))
      //获取下一行的“dwd_insert_time”值(如果存在)并作为当前行的“dwd_insert_time”值。
      .withColumn("dwd_insert_time", lead("dwd_insert_time", 1).over(windowSpec))
      //如果当前行的“dwd_insert_time”为空,则使用“dwd_modify_time”列的值作为替代。
      .withColumn("dwd_insert_time", when($"dwd_insert_time".isNull, $"dwd_modify_time").otherwise($"dwd_insert_time"))
      .where($"sordId" === 1)  //只保留sortId 为1的字段 ,之前也就是只保留新字段数据
      .drop("sordId")
      .write
      .mode(SaveMode.Overwrite) //覆盖之前的数据
      .saveAsTable("dwd.zhang_dim_sku_info")  //将最终清洗完成的数据写入zhang_dim_sku_info表

完整代码

package qingxi.zengliang
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.{SaveMode, SparkSession}
import org.apache.spark.sql.functions.{col, desc, from_unixtime, lead, lit, row_number,  unix_timestamp, when}
/**
 * 抽取ods库sku_info表中昨天的分区(子任务一生成的分区)数据,并结合dim_sku_info最新分区现有的数据,
 * 根据id合并数据到dwd库中dim_sku_info的分区表(合并是指对dwd层数据进行插入或修改,需修改的数据以id为合并字段,根据create_time排序取最新的一条),
 * 分区字段为etl_date且值与ods库的相对应表该值相等,并添加dwd_insert_user、dwd_insert_time、dwd_modify_user、dwd_modify_time四列,
 * 其中dwd_insert_user、dwd_modify_user均填写“user1”。若该条数据第一次进入数仓dwd层则dwd_insert_time、dwd_modify_time均填写当前操作时间,
 * 并进行数据类型转换。若该数据在进入dwd层时发生了合并修改,则dwd_insert_time时间不变,dwd_modify_time存当前操作时间,
 * 其余列存最新的值。使用hive cli查询表dim_sku_info的字段id、sku_desc、dwd_insert_user、dwd_modify_time、etl_date,
 * 条件为最新分区的数据,id大于等于15且小于等于20,并且按照id升序排序,
 */
object Test02 {
  def main(args: Array[String]): Unit = {
    //spark API
    Logger.getLogger("org").setLevel(Level.OFF)
    System.setProperty("HADOOP_USER_NAME", "root")
    val sparkConf = new SparkConf()
      .setAppName("qingxi")
      .setMaster("local[*]")
    val sparkSession = SparkSession.builder()
      .config(sparkConf)
      .config("hive.metastore.uris", "thrift://bigdata1:9083")
      .config("dfs.client.use.datanode.hostname", "true")
      .config("spark.sql.parquet.writeLegacyFormat", "true")
      .enableHiveSupport()
      .getOrCreate()
    // 全量抽取数据到dwd
//      sparkSession.read.table("ods.sku_info")
//        .withColumn("dwd_insert_user",lit("user1"))
//        .withColumn("dwd_insert_time",lit(from_unixtime(unix_timestamp(),"yyyy-MM-dd HH:mm:ss")))
//        .withColumn("dwd_modify_user",lit("user1"))
//        .withColumn("dwd_modify_time",lit(from_unixtime(unix_timestamp(),"yyyy-MM-dd HH:mm:ss")))
//        .write
//        .mode(SaveMode.Overwrite)
//        .partitionBy("etl_date")
//        .saveAsTable("dwd.dim_sku_info")
//        sparkSession.sql("select * from dwd.dim_sku_info").show()
//        sparkSession.sql("select count(*) from dwd.dim_sku_info").show()
  // dwd 清洗过后数据
    import sparkSession.implicits._
    val dwd_sku_info = sparkSession.table("dwd.dim_sku_info")
    //将ods 表中数据清洗
    val ods_sku_info = sparkSession.table("ods.sku_info")
      .withColumn("dwd_insert_user", lit("user1"))
      .withColumn("dwd_insert_time", lit(from_unixtime(unix_timestamp(), "yyyy-MM-dd HH:mm:ss")))
      .withColumn("dwd_modify_user", lit("user1"))
      .withColumn("dwd_modify_time", lit(from_unixtime(unix_timestamp(), "yyyy-MM-dd HH:mm:ss")))
    // 合并表
    val windowSpec = Window.partitionBy("id").orderBy(desc("create_time"))
    val union_table = ods_sku_info.select(dwd_sku_info.columns.map(col): _*).union(dwd_sku_info)
      .withColumn("sordId", row_number().over(windowSpec))
      .withColumn("dwd_insert_time", lead("dwd_insert_time", 1).over(windowSpec))
      .withColumn("dwd_insert_time", when($"dwd_insert_time".isNull, $"dwd_modify_time").otherwise($"dwd_insert_time"))
      .where($"sordId" === 1)
      .drop("sordId")
      .write
      .mode(SaveMode.Overwrite)
      .saveAsTable("dwd.zhang_dim_sku_info")
    sparkSession.stop()
  }
}

清洗结果
在这里插入图片描述
使用hive cli查询表zang_dim_sku_info的字段id、sku_desc、dwd_insert_user、dwd_modify_time、etl_date,条件为最新分区的数据,id大于等于15且小于等于20,并且按照id升序排序
在这里插入图片描述

总结

清洗数据是一个重要的预处理步骤,它有助于提高数据的质量和准确性。
首先,你需要理解数据的来源、内容和结构。这包括了解每个字段的含义、数据类型以及是否存在缺失值、异常值或重复值。

数据清洗:

缺失值处理:根据缺失值的数量和分布,可以选择填充缺失值、删除含有缺失值的记录或使用特定的算法来处理。
异常值处理:异常值可能是由于数据输入错误、设备故障或其他原因引起的。对于异常值,可以手动检查并修正,或者使用统计方法(如Z-score或IQR)来识别并处理。
重复值处理:如果数据中存在重复记录,可以根据业务需求进行删除或合并。
数据转换:将数据从一种格式或结构转换为另一种,以便于后续的分析或建模。例如,将日期从字符串转换为日期格式。

数据规范化:确保数据在相同的尺度上,以便于比较和分析。例如,对分类变量进行编码,或者对连续变量进行归一化。

数据整合:如果数据来自多个来源或格式,需要将它们整合到一起,确保数据的一致性和完整性。

数据验证:在清洗过程中,需要不断验证数据的准确性。这可以通过与原始数据进行对比、使用业务逻辑进行检查等方式实现。

文档记录:在整个清洗过程中,建议详细记录每一步的操作和结果,以便于后续的复查和验证。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值