清洗要求
抽取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)来识别并处理。
重复值处理:如果数据中存在重复记录,可以根据业务需求进行删除或合并。
数据转换:将数据从一种格式或结构转换为另一种,以便于后续的分析或建模。例如,将日期从字符串转换为日期格式。
数据规范化:确保数据在相同的尺度上,以便于比较和分析。例如,对分类变量进行编码,或者对连续变量进行归一化。
数据整合:如果数据来自多个来源或格式,需要将它们整合到一起,确保数据的一致性和完整性。
数据验证:在清洗过程中,需要不断验证数据的准确性。这可以通过与原始数据进行对比、使用业务逻辑进行检查等方式实现。
文档记录:在整个清洗过程中,建议详细记录每一步的操作和结果,以便于后续的复查和验证。