客户数据指标开发
物流字典码表数据类型定义
为了后续使用方便且易于维护,根据物流字典表的数据类型定义成枚举工具类,物流字典表的数据如下:
来自:tbl_codes表
| name | type |
|---|---|
| 注册渠道 | 1 |
| 揽件状态 | 2 |
| 派件状态 | 3 |
| 快递员状态 | 4 |
|---|---|
| 地址类型 | 5 |
| 网点状态 | 6 |
| 员工状态 | 7 |
| 是否保价 | 8 |
| 运输工具类型 | 9 |
| 运输工具状态 | 10 |
| 仓库类型 | 11 |
| 是否租赁 | 12 |
| 货架状态 | 13 |
| 回执单状态 | 14 |
| 出入库类型 | 15 |
| 客户类型 | 16 |
| 下单终端类型 | 17 |
| 下单渠道类型 | 18 |
客户主题
需求分析
| 指标 | 计算公式 | 备注 |
|---|---|---|
| 总客户数 | 下单总人数 | 1人1天多次下单 |
| 今日新增客户数 | 今日注册下单总人数 | |
| 留存用户总数 | 用户最后下单日期距今天数小于等于180 | |
| 留存率(超过180天未下单表示已流失,否则表示留存) | 用户最后下单日期距今天数小于等于180 | |
| 活跃用户数(近10天内有发件的客户表示活跃用户) | 用户最后下单日期距今天数小于等于10 | |
| 月度新用户数 | 用户当月下单总人数 | |
| 沉睡用户数(3个月~6个月之间的用户表示已沉睡) | 用户最后下单日期距今天数:3个月 ~ 6 个月 | |
| 流失用户数(9个月未下单表示已流失) | 用户最后下单日期距今天数 大于270天 | |
| 客单数 | 每个用户下单数 | |
| 客单价 | 每个用户单价 | |
| 平均客单数 | 总单数除以下单人数 |
数据字典
| 表 | 字段名 | 别名 | 字段描述 |
|---|---|---|---|
| tbl_customer | id | id | 客户ID |
| tbl_customer | name | name | 客户姓名 |
| tbl_customer | tel | tel | 客户电话 |
| tbl_customer | mobile | mobile | 客户手机 |
| tbl_customer | 客户邮箱 | ||
| tbl_customer | type | type | 客户类型ID |
| tbl_codes | codeDesc | type_name | 客户类型名称 |
| tbl_customer | isownreg | is_own_reg | 是否自行注册 |
| tbl_customer | regdt | regdt | 注册时间 |
| tbl_customer | regchannelid | reg_channel_id | 注册渠道ID |
| tbl_customer | state | state | 客户状态ID |
| tbl_customer | cdt | cdt | 创建时间 |
| tbl_customer | udt | udt | 修改时间 |
| tbl_customer | lastlogindt | last_login_dt | 最后登录时间 |
| tbl_customer | remark | remark | 备注 |
| tbl_consumer_sender_info | cdt | first_cdt | 首次下单时间 |
| tbl_consumer_sender_info | cdt | last_cdt | 尾次下单时间 |
| tbl_express_package | billCount | billCount | 下单总数 |
| tbl_express_package | totalAmount | totalAmount | 累计下单金额 |
| tbl_customer | yyyyMMdd(cdt) | day | 创建时间年月日格式 |
实现步骤:
实现步骤:
| 在dwd目录下创建 CustomerDWD 单例对象,继承自AbstractOfflineApp特质
| 初始化环境的参数,创建SparkSession对象
| 获取客户表(tbl_customer)数据,并缓存数据
| 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)
| 获取客户寄件信息表(tbl_consumer_sender_info)数据,并缓存数据
| 获取客户包裹表(tbl_express_package)数据,并缓存数据
| 获取物流字典码表(tbl_codes)数据,并缓存数据
| 根据以下方式拉宽仓库车辆明细数据
| 根据客户id,在客户表中获取客户数据
| 根据包裹id,在包裹表中获取包裹数据
| 根据客户类型id,在物流字典码表中获取客户类型名称数据
| 创建客户明细宽表(若存在则不创建)
| 将客户明细宽表数据写入到数据表中
| 删除缓存数据
代码
package cn. manor.logistics.offline.dws
import cn. manor.logistics.common.OfflineTableDefine import cn. manor.logistics.offline.AbstractOfflineApp import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Row}
import org.apache.spark.sql.functions.{current_date, date_format, date_sub, sum, trunc} import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType}
/**
*客户主题指标开发:
*从表加载客户详情宽表数据,按照具体业务进行统计分析,最后结果存储数据库中DWS层。
*/
object CustomerDWS extends AbstractOfflineApp{
/**
*对数据集DataFrame按照业务需求编码,由子类复杂实现
*
*@param dataframe 数据集,表示加载事实表的数据
*@retur| 处理以后数据集
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
import dataframe.sparkSession.implicits._
/**
* TODO: 针对客户主题数据宽表来说,每天都是全量更新的,计算指标时加载全量数据进行计算的
*/
// 指标一:总客户数
val customerCount: Long = dataframe.count()
// 指标二:今日新增客户数(注册时间为今天)
val additionCustomerCount: Long = dataframe
.where(
date_format($"regdt", "yyyy-MM-dd") === date_sub(current_date(), 1)
)
.count()
// 指标三:留存数(超过180天未下单表示已流失,否则表示留存) 和留存率
val preserveCustomerCount: Long = dataframe
.where($"last_sender_cdt" <= date_sub(current_date(), 180))
.count()
val preserveRate: Double = preserveCustomerCount / customerCount.toDouble
// 指标四:活跃用户数(近10天内有发件的客户表示活跃用户) val activeCustomerCount: Long = dataframe
.where($"last_sender_cdt" <= date_sub(current_date(), 10))
.count()
// 指标五:月度新用户数
val monthOfNewCustomerCount: Long = dataframe
.where(
$"regDt".between(
trunc($"regDt", "mm"), date_format(current_date(), "yyyy-MM-dd")
)
)
.count()
// 指标六:沉睡用户数(3个月~6个月之间的用户表示已沉睡) val sleepCustomerCount = dataframe
.where(
"last_sender_cdt >= date_sub(now(), 90) and last_sender_cdt <= date_sub(now(), 180)"
)
.count()
// 指标七:流失用户数(9个月未下单表示已流失) val loseCustomerCount: Long = dataframe
.where("last_sender_cdt >= date_sub(now(), 270)")
.count()
// 指标八:客单价、客单数、平均客单数
val customerBillDF: DataFrame = dataframe.where("first_sender_id is not null")
// 客单数
val customerBillCount: Long = customerBillDF.count()
// 客单价 = 总金额/总单数
val customerBillAggDF: DataFrame = customerBillDF.agg( sum($"totalCount").as("sumCount"), // 总单数sum($"totalAmount").as("sumAmount") // 总金额
)
val billAggRow: Row = customerBillAggDF.first()
// 客单价 = 总金额/总单数
val customerAvgAmount: Double=billAggRow.getAs[Double]("sumAmount")/billAggRow.getAs[Long]("sumCount")
// 平均客单数
val avgCustomerBillCount: Double = billAggRow.getAs[Long]("sumCount") / customerBillCount.toDouble
// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
val dayValue: String = dataframe
.select(
date_format(current_date(), "yyyyMMdd").cast(StringType)
)
.limit(1).first().getAs[String](0) val aggRow: Row = Row(
dayValue, // customerCount, // additionCustomerCount, // preserveRate, // activeCustomerCount, //
monthOfNewCustomerCount, // sleepCustomerCount, // loseCustomerCount, // customerBillCount, // customerAvgAmount, // avgCustomerBillCount
)
// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(Seq(aggRow))
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false)
.add("customerCount", LongType, nullable = true)
.add("additionCustomerCount", LongType, nullable = true)
.add("preserveRate", DoubleType, nullable = true)
.add("activeCustomerCount", LongType, nullable = true)
.add("monthOfNewCustomerCount", LongType, nullable = true)
.add("sleepCustomerCount", LongType, nullable = true)
.add("loseCustomerCount", LongType, nullable = true)
.add("customerBillCount", LongType, nullable = true)
.add("customerAvgAmount", DoubleType, nullable = true)
.add("avgCustomerBillCount", DoubleType, nullable = true)
// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame
val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)
// 返回聚合数据
aggDF
}
def main(args: Array[String]): Unit = {
// 调用模板方法,进行数据加载、计算和保存
execute(
this.getClass, OfflineTableDefine.CUSTOMER_DETAIL, // OfflineTableDefine.CUSTOMER_SUMMERY, isLoadFullData = true //
)
}
}
快递单指标开发
数据字典
| 表 | 字段名 | 别名 | 字段描述 |
|---|---|---|---|
| tbl_express_bill | id | id | 快递单id |
| tbl_express_bill | expressNumber | express_number | 快递单编号 |
|---|---|---|---|
| tbl_express_bill | cid | cid | 客户ID |
| tbl_customer | name | cname | 客户名字 |
| tbl_address | detailAddr | caddress | 详细地址 |
| tbl_express_bill | eid | eid | 员工ID |
| tbl_courier | name | ename | 快递员姓名 |
| tbl_dot | id | dot_id | 网点ID |
| tbl_dot | dotName | dot_name | 网点名称 |
| tbl_company | companyName | company_name | 公司名字 |
| tbl_express_bill | orderChannelId | order_channel_id | 下单渠道ID |
| tbl_codes | channelTypeName | order_channel_name | 下单渠道名称 |
| tbl_express_bill | orderDt | order_dt | 下单时间 |
| tbl_express_bill | orderTerminalType | order_terminal_type | 下单设备类型ID |
| tbl_codes | orderTypeName | order_terminal_type_name | 下单设备类型名称 |
| tbl_express_bill | orderTerminalOsType | order_terminal_os_type | 下单设备操作系统ID |
| tbl_express_bill | reserveDt | reserve_dt | 预约取件时间 |
| tbl_express_bill | isCollectPackageTimeout | is_collect_package_timeout | 是否取件超时 |
| tbl_express_bill | timeoutDt | timeout_dt | 超时时间 |
| tbl_customer | type | type | 客户类别id |
| tbl_express_bill | cdt | cdt | 创建时间 |
| tbl_express_bill | udt | udt | 修改时间 |
| tbl_express_bill | remark | remark | 备注 |
| tbl_express_bill | yyyyMMdd(cdt) | day | 创建时间 年月日格式 |
实现步骤:
| 在dws目录下创建 ExpressBillDWS 单例对象,继承自BasicOfflineApp特质
| 初始化环境的参数,创建SparkSession对象
| 根据指定的日期获取拉宽后的快递单宽表(tbl_express_bill_detail)增量数据,并缓存数据
| 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)
| 指标计算
| 计算总快递单数
| 各类客户快递单数
| 各类客户最大快递单数
| 各类客户最小快递单数
| 各类客户平均快递单数
| 各网点快递单数
| 各网点最大快递单数
| 各网点最小快递单数
| 各网点平均快递单数
| 各渠道快递单数
| 各渠道最大快递单数
| 各渠道最小快递单数
| 各渠道平均快递单数
| 各终端快递单数
| 各终端最大快递单数
| 各终端最小快递单数
| 各终端平均快递单数
| 获取当前时间yyyyMMddHH
| 构建要持久化的指标数据(需要判断计算的指标是否有值,若没有需要赋值默认值)
| 通过StructType构建指定Schema
| 创建快递单指标数据表(若存在则不创建)
| 持久化指标数据到表
代码
package cn. manor.logistics.offline.dws
import cn. manor.logistics.common.{Configuration, OfflineTableDefine, SparkUtils} import cn. manor.logistics.offline.BasicOfflineApp
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType} import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
import org.apache.spark.storage.StorageLevel import scala.collection.mutable.ListBuffer
/**
*快递单主题开发:
*加载Kudu中快递单宽表:dwd_tbl_express_bill_detail 数据,按照业务进行指标统计
*/
object ExpressBillDWS extends BasicOfflineApp{
/**
*数据处理,依据指标进行分析
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
val spark: SparkSessio| = dataframe.sparkSessio| import spark.implicits._
// 由于多个指标分析,宽表数据被使用多次,需要缓存dataframe.persist(StorageLevel.MEMORY_AND_DISK) if(dataframe.isEmpty){
logWarning(s"Kudu数据库DWD层[${OfflineTableDefine.EXPRESS_BILL_DETAIL}]表,没有加载到数据,请确认有数据在执
行 ")
}
System.exit(-1)
// TODO: 假设每次加载宽表的数据都是全量的,离线分析按照每天聚合计算,将数据划分为不同天,针对每天的数据进行聚合计算
// 定义集合列表,可变的列表,将每天计算指标数据Row存储
val rowList: ListBuffer[Row] = ListBuffer[Row]() dataframe.select($"day").distinct().collect().foreach{dayRow =>
// 获取具体某天的值
val day: String = dayRow.getAs[String](0) // 1210, 1211, 1212
// 按照day进行过滤获取每天的数据,进行指标的计算
val expressBillDetailDF: Dataset[Row] = dataframe.filter($"day" === day)
// 按照业务指标计算
// 指标一:计算总快递单数
val expressBillTotalDF: DataFrame = expressBillDetailDF.agg(count($"id").as("total"))
// 指标二:各类客户快递单数,最大、最小、平均
val expressBillTypeTotalDF: DataFrame = expressBillDetailDF.groupBy($"type").count() val expressBillTypeTotalAggDF: DataFrame = expressBillTypeTotalDF.agg(
max($"count").as("maxTypeTotal"), // min($"count").as("minTypeTotal"), // round(avg($"count").as("avgTypeTotal"), 0) //
)
// 各网点快递单数指标三:,最大、最小、平均
val expressBillDotTotalDF: DataFrame = expressBillDetailDF.groupBy($"dot_id").count() val expressBillDotTotalAggDF: DataFrame = expressBillDotTotalDF.agg(
max($"count").as("maxDotTotal"), // min($"count").as("minDotTotal"), // round(avg($"count").as("avgDotTotal"), 0) //
)
// 指标四:各渠道快递单数,最大、最小、平均
val expressBillChannelTotalDF: DataFrame = expressBillDetailDF.groupBy($"order_channel_id").count() val expressBillChannelTotalAggDF: DataFrame = expressBillChannelTotalDF.agg(
max($"count").as("maxChannelTotal"), // min($"count").as("minChannelTotal"), // round(avg($"count").as("avgChannelTotal"), 0) //
)
// 指标五:各终端快递单数,最大、最小、平均
val expressBillTerminalTotalDF: DataFrame = expressBillDetailDF.groupBy($"order_terminal_type").count() val expressBillTerminalTotalAggDF: DataFrame = expressBillTerminalTotalDF.agg(
max($"count").as("maxTerminalTotal"), // min($"count").as("minTerminalTotal"), // round(avg($"count").as("avgTerminalTotal"), 0) //
)
// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
/*
Row对象创建方式2种:
-1. Row(v1, v2, v3, )
-2. Row.fromSeq(Seq(v1, v2, v3, ))
*/
val aggRow: Row = Row.fromSeq( dayRow.toSeq ++
expressBillTotalDF.first().toSeq ++ expressBillTypeTotalAggDF.first().toSeq ++ expressBillDotTotalAggDF.first().toSeq ++ expressBillChannelTotalAggDF.first().toSeq ++ expressBillTerminalTotalAggDF.first().toSeq
}
/**
)
// 将每天聚合计算结果加入列表中
rowList += aggRow
*问题:如何将列表中数据转换为DataFrame
*采用并行化方向将列表(Seq类型对象)转换为RDD
*RDD转换为DataFrame: 反射RDD[CaseClass] 和 自定义Schema-> RDD[Row]和Schema
*/
// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(rowList.toList) // 将可变集合对象转换为不可变的
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false) // 针对每天数据进行聚合得到一个结果,设置day为结果表中id
.add("total", LongType, nullable = true)
.add("maxTypeTotal", LongType, nullable = true)
.add("minTypeTotal", LongType, nullable = true)
.add("avgTypeTotal", DoubleType, nullable = true)
.add("maxDotTotal", LongType, nullable = true)
.add("minDotTotal", LongType, nullable = true)
.add("avgDotTotal", DoubleType, nullable = true)
.add("maxChannelTotal", LongType, nullable = true)
.add("minChannelTotal", LongType, nullable = true)
.add("avgChannelTotal", DoubleType, nullable = true)
.add("maxTerminalTotal", LongType, nullable = true)
.add("minTerminalTotal", LongType, nullable = true)
.add("avgTerminalTotal", DoubleType, nullable = true)
// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)
// 返回计算指标结果数据
aggDF
}
// SparkSQL 应用程序入口
def main(args: Array[String]): Unit = {
// step1. 构建SparkSession实例对象,传递SparkConf对象
val spark: SparkSessio| = SparkUtils.createSparkSession( SparkUtils.autoSettingEnv(SparkUtils.sparkConf()), this.getClass
)
import spark.implicits._ spark.sparkContext.setLogLevel(Configuration.LOG_OFF)
// step2. 加载快递单宽表数据
val expressBillDetailDF: DataFrame = load(
spark, OfflineTableDefine.EXPRESS_BILL_DETAIL, isLoadFullData = Configuration.IS_FIRST_RUNNABLE
)
expressBillDetailDF.show(10, truncate = false)
// step3. 按照业务指标开发
val expressBillSummaryDF: DataFrame = process(expressBillDetailDF) expressBillSummaryDF.show(10, truncate = false)
// step4. 保存分析指标结果数据
save(expressBillSummaryDF, OfflineTableDefine.EXPRESS_BILL_SUMMARY)
// step5. 应用结束,关闭资源
spark.stop()
}
}
车辆指标开发
数据字典
| 表 | 字段名 | 别名 | 字段描述 |
|---|---|---|---|
| tbl_transport_tool | id | id | 运输工具ID |
| tbl_transport_tool | brand | brand | 运输工具品牌 |
| tbl_transport_tool | model | model | 运输工具型号 |
| tbl_transport_tool | type | type | 运输工具类型 |
| tbl_codes | codeDesc/ttTypeName | type_name | 车辆类型描述 |
| tbl_transport_tool | givenLoad | given_load | 额定载重 |
| tbl_transport_tool | loadCnUnit | load_cn_unit | 中文载重单位 |
| tbl_transport_tool | loadEnUnit | load_en_unit | 英文载重单位 |
| tbl_transport_tool | buyDt | buy_dt | 购买时间 |
| tbl_transport_tool | licensePlate | license_plate | 牌照 |
| tbl_transport_tool | state | state | 运输工具状态 |
| tbl_codes | codeDesc/ttStateName | state_name | 运输工具状态描述 |
| tbl_transport_tool | cdt | cdt | 创建时间 |
| tbl_transport_tool | udt | udt | 修改时间 |
| tbl_transport_tool | remark | remark | 备注 |
| tbl_dot | id | dot_id | 网点id |
| tbl_dot | dotNumber | dot_number | 网点编号 |
| tbl_dot | dotName | dot_name | 网点名称 |
| tbl_dot | dotAddr | dot_addr | 网点地址 |
| tbl_dot | dotGisAddr | dot_gis_addr | 网点GIS地址 |
| tbl_dot | dotTel | dot_tel | 网点电话 |
| tbl_dot | manageAreaId | manage_area_id | 网点管理辖区ID |
| tbl_dot | manageAreaGis | manage_area_gis | 网点管理辖区地理围栏 |
| tbl_company | id | company_id | 公司ID |
| tbl_company | companyName | company_name | 公司名称 |
|---|---|---|---|
| tbl_company | cityId | city_id | 城市ID |
| tbl_company | companyNumber | company_number | 公司编号 |
| tbl_company | companyAddr | company_addr | 公司地址 |
| tbl_company | companyAddrGis | company_addr_gis | 公司gis地址 |
| tbl_company | companyTel | company_tel | 公司电话 |
| tbl_company | isSubCompany | is_sub_company | 母公司ID |
| tbl_transport_tool | yyyyMMdd(cdt) | day | 创建时间,年月日格式 |
实现步骤:实现步骤:
l 在dws目录下创建 TransportToolWarehouseDWS 单例对象,继承自AbstractOfflineApp特质
l 初始化环境的参数,创建SparkSession对象
l 根据指定的日期获取拉宽后的车辆主题宽表(ttbl_warehouse_transport_tool_detail)增量数据,并缓存数据
l 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)
l 指标计算
l 构建要持久化的指标数据(需要判断计算的指标是否有值,若没有需要赋值默认值)
l 通过StructType构建指定Schema
| 各网点发车次数
| 各网点最大发车次数
| 各网点最小发车次数
| 各网点平均发车次数
| 各区域发车次数
| 各区域最大发车次数
| 各区域最小发车次数
| 各区域平均发车次数
| 各公司发车次数
| 各公司最大发车次数
| 各公司最小发车次数
| 各公司平均发车次数
| 获取当前时间yyyyMMddHH
l 创建车辆主题指标数据表(若存在则不创建)
l 持久化指标数据到表
代码
package cn. manor.logistics.offline.dws
import cn. manor.logistics.common.{Configuration, OfflineTableDefine} import cn. manor.logistics.offline.AbstractOfflineApp
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row}
import org.apache.spark.sql.functions.{avg, max, min, round, sum}
import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType} import scala.collection.mutable.ListBuffer
/**
*仓库车辆相关指标聚合统计:
*从Kudu表加载仓库车辆详细宽表数据,按照不同维度进行分组,计算相关指标。
*/
object TransportToolWarehouseDWS extends AbstractOfflineApp{
/**
*对数据集DataFrame按照业务需求编码,由子类复杂实现
*
*@param dataframe 数据集,表示加载事实表的数据
*@retur| 处理以后数据集
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
import dataframe.sparkSession.implicits._
// dataframe 表示从Kudu表加载宽表数据:tbl_warehouse_transport_tool_detail val rowList: ListBuffer[Row] = ListBuffer[Row]() dataframe.select($"day").collect().foreach{dayRow =>
// 获取具体日期day
val day: String = dayRow.getAs[String](0)
// 依据day过滤出对应宽表数据
val ttWsDetailDF: Dataset[Row] = dataframe.filter($"day" === day)
// 指标计算
// 指标一:各网点发车次数及最大、最小和平均
val ttWsTotalDF: DataFrame = ttWsDetailDF.groupBy($"ws_id").count() val ttWsTotalAggDF: DataFrame = ttWsTotalDF.agg(
sum($"count").as("sumDotTotal"), // max($"count").as("maxDotTotal"), // min($"count").as("minDotTotal"), // round(avg($"count"), 0).as("avgDotTotal") //
)
// 指标二:各区域发车次数及最大、最小和平均
val ttCityTotalDF: DataFrame = ttWsDetailDF.groupBy($"city_id").count() val ttCityTotalAggDF: DataFrame = ttCityTotalDF.agg(
sum($"count").as("sumCityTotal"), // max($"count").as("maxCityTotal"), // min($"count").as("minCityTotal"), // round(avg($"count"), 0).as("avgCityTotal") //
)
// 指标三:各公司发车次数及最大、最小和平均
val ttCompanyTotalDF: DataFrame = ttWsDetailDF.groupBy($"company_id").count() val ttCompanyTotalAggDF: DataFrame = ttCompanyTotalDF.agg(
sum($"count").as("sumCompanyTotal"), // max($"count").as("maxCompanyTotal"), // min($"count").as("minCompanyTotal"), // round(avg($"count"), 0).as("avgCompanyTotal") //
)
// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
val aggRow: Row = Row.fromSeq( dayRow.toSeq ++ //
ttWsTotalAggDF.first().toSeq ++ // ttCityTotalAggDF.first().toSeq ++ // ttCompanyTotalAggDF.first().toSeq //
)
// 将每天聚合计算结果加入列表中
rowList += aggRow
}
// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(rowList.toList) // 将可变集合对象转换为不可变的
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false) // 针对每天数据进行聚合得到一个结果,设置day为结果表中id
.add("sumWsTotal", LongType, nullable = true)
.add("maxWsTotal", LongType, nullable = true)
.add("minWsTotal", LongType, nullable = true)
.add("avgWsTotal", DoubleType, nullable = true)
.add("sumCityTotal", LongType, nullable = true)
.add("maxCityTotal", LongType, nullable = true)
.add("minCityTotal", LongType, nullable = true)
.add("avgCityTotal", DoubleType, nullable = true)
.add("sumCompanyTotal", LongType, nullable = true)
.add("maxCompanyTotal", LongType, nullable = true)
.add("minCompanyTotal", LongType, nullable = true)
.add("avgCompanyTotal", DoubleType, nullable = true)
// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)
// 返回聚合数据
aggDF
}
def main(args: Array[String]): Unit = {
// 调用execute方法,传递Kudu表名称及是否第一次运行
execute(
this.getClass, OfflineTableDefine.WAREHOUSE_TRANSPORT_TOOL_DETAIL, // OfflineTableDefine.WAREHOUSE_TRANSPORT_TOOL_SUMMARY, // isLoadFullData = Configuration.IS_FIRST_RUNNABLE //
)
}
}