新零售项目
1.新零售概述
1.1行业背景
早起基本上都是一些线下店铺:
阶段一:百货商店
阶段二:超级市场
阶段三:连锁商店
阶段四:电子商务(网络销售--网店)
阶段五:新零售(线上+线下+物流)
1.2业务模块介绍
1.商品发布流程
2.单店铺订单流程
3.购物车流程
4.配送流程图
5.退货业务流程图
6.项目架构图
- 基于cloudera manager构建的大数据分析平台, 在此平台基础上, 构建有 HDFS, YARN, zookeeper,
sqoop, oozie, HUE, HIVE 等相关的大数据组件, 同时为了提升分析的效率. 引入presto来进行分析处理操作,
使用FineBi实现图表展示操作, 整个分析工作是一个周而复始, 不断的干, 采用oozie完成任务的调度工作 - 数据流转的流程: 整个项目的数据源都是集中在MySQL中的, 通过sqoop完成数据的导入操作, 将数据导入到HDFS中,
使用HIVE构建相关的表, 建立数仓体系, 在HIVE进行分层处理, 在进行统计分析的时候, 采用presto提升分析的效率,
将分析的结果导出到Mysql中, 最后使用fineBi完成报表展示操作, 整个项目基于cloudera manager进行监控管理,
使用oozie完成工作流的调度操作
1.3clouderamanager工具介绍
- cloudera manager 是一款大数据的统一监控管理平台, 此平台主要是对cloudera公司旗下CDH版本软件进行管理工作,
- 提供的服务: 统一的监控, 自动化部署, 对CDH软件进行相关管理. 产生原因: apache版本软件所存在弊端:
部署过程极其复杂,超过20个节点的时候,手动部署已经超级累 各个组件部署完成后,各个为政,没有统一化管理界面
组件和组件之间的依赖关系很复杂,一环扣一环,部署过程心累
各个组件之间没有统一的metric可视化界面,比如说hdfs总共占用的磁盘空间、IO、运行状况等
优化等需要用户自己根据业务场景进行调整(需要手工的对每个节点添加更改配置,效率极低,我们希望的是一个配置能够自动的分发到所有的节点上)
市场需要有一款统一软件, 对大数据中各个组件进行统一化管理操作, 由此产生cloudera manager 另一个原因:
cloudera 公司希望能够有更多客户使用CDH版本, 降低客户使用难度
1.4项目环境部署操作
- 将资料中提供虚拟机压缩包, 解压到一个没有中文没有空格, 以及磁盘空间相对充足的磁盘中(大于100GB)
- 修改VMware的网卡设置: 统一修改为 88网段, 网关为192.168.88.2
- 在windows的hosts文件中, 添加映射信息: C:\Windows\System32\drivers\etc
添加以下内容:
192.168.88.80 hadoop01 hadoop01.itcast.cn
192.168.88.81 hadoop02 hadoop02.itcast.cn
4)连接cloudera manager
http://hadoop01:7180
用户名: admin
密码: admin
注意: 后续关闭虚拟机: 务必使用shutdown -h now | init 0 万万不可直接强制关机
重启虚拟机: 使用reboot命令
长时间不使用虚拟机, 建议将其关机(尤其是使用机械硬盘的) 比如中午的时候
启动服务器后, 并不能立即访问, 需要等待大约 5~15分钟的时候后, 才可以正常访问 7180
1.5数据仓库的基本概念
1- 什么是数据仓库呢?
存储数据的仓库, 主要用于存储过去历史发生过的数据,面向主题, 对数据进行统计分析的操作, 从而能够对未来提供决策支持
2- 数据仓库最大的特点是什么呢?
数据仓库既不生产数据, 也不消耗数据, 数据来源于各个数据源
3- 数据仓库的四大特征:
- 1- 面向主题: 分析什么 什么就是我们的主题
- 2- 集成性: 数据从各个数据源汇聚而来, 数据的结构都不一定一样
- 3- 非易失性(稳定性): 存储都是过去历史的数据, 不会发送变更, 甚至某些数据仓库都不支持修改操作
- 4- 时变性: 随着时间推移, 将最近发生的数据也需要放置到数据仓库中, 同时分析的方案也无法满足当前需求, 需要变更分析的手段
4- OLAP 和 OLTP区别:
5- 什么是ETL:
- ETL: 抽取 转换 加载
- 狭义上ETL: 指的数据从ODS层抽取出来, 对ODS层的数据进行清洗转换处理的操作, 将清洗转换后的数据加载到DW层过程
- 宽泛的ETL: 指的是数仓的全过程
6- 什么是数据仓库 和 数据集市
- 数据仓库是包含数据集市的, 在一个数据仓库中可以有多个数据集市
- 数据仓库: 一般指的构建集团数据中心, 基于业务形成各种业务的宽表或者统计宽表
- 数据集市: 基于部门或者基于主题, 形成主题或者部门相关的统计宽表
2.维度分析
2.1维度分析的基本介绍
1.什么是维度?
- 看待问题角度, 当我们对一个主题进行分析的时候, 可以从不同的角度来分析, 这些角度就是维度 比如说: 对订单进行分析 可以从 用户,
时间, 地区, 商家, 商圈…
维度的分类:
-
定性维度: 一般指的统计 每个 各个 这种维度, 比如 统计每天 每小时 各个用户… 这种维度在编写SQL, 一般是放置在
group by -
定量维度: 一般指的统计某个范围, 或者某个具体值的, 比如 统计年龄在18~60岁, 时间为2021年度,这种维度在编写 SQL, 一般是放置在 where条件
-
上卷 和 下钻: 比如说 我们以天作为标准, 上卷统计 周 月 年 下钻统计: 小时
-
分层或者分级: 比如说: 以地区为例, 将地区划分为 省份 市 县/区
2.什么是指标
- 衡量事务发展的标准,也叫度量, 简单来说: 在根据维度进行分析的时候, 必然要分析出一些结果, 这个结果就是度量
常见的度量值有那些:
- count(), sum(), min(),max(),avg()
指标的分类:
- 绝对指标: 指的统计计算一个具体的值, 比如说 销售额, 订单量
- 必须要对全部的数据进行统计处理
- count(), sum(), min(),max(),avg()
- 相对指标: 指的统计相对的结果, 比如说 同比增长 环比增长 流失率 增长率…
- 这些指标在计算的时候, 是可以不需要对全部数据进行统计,可以通过抽样的方式来计算即可
例子:
需求:
- 请统计在2021年度, 来自于北京 女性 未婚, 年龄在 18 ~28 之间的每天的销售总额是多少? 分析:
涉及到维度: 时间维度, 地区维度, 性别, 婚姻状态, 年龄
定性维度: 每天
定量维度: 2021, 北京 女性 未婚 年龄
涉及指标: 销售额(绝对指标)
sum()
SQL:
select day, sum(price) from where 时间 = 2021 and address = ‘北京’ and sex = ‘女性’ and status = ‘未婚’ and age between 18 and 28 group by day;
3.数仓建模
3.1数仓建模概念
如何在数据仓库中构建表, 是一套用于规范化建表的理论
3.2常见的数仓建模理论
- 三范式建模: 主要是应用在传统的数据仓库中, 指的数据存储在关系型的数据库中, 要求在建表的时候, 表必须有主键
同时表中尽量的避免数据的冗余的发生, 尽可能拆分表 - 维度建模: 主要是应用于新型的数据仓库中, 指的数据存储在专门用于进行OLAP数据库中, 比如 要求建模的时候以分析为前提,
只要是利于分析的建模方案, 认为都是OK的, 在此情况下, 即使数据存在一定的冗余也是OK的
后续主要采用维度建模的思想来构建相关的表, 在维度建模中, 主要规定了两种表模型: 事实表 和 维度表
3.3事实表
事实表: 指的主题,要统计的主题是什么, 对应事实就是什么, 而主题所对应的表, 其实事实表
- 事实表一般是一坨主键(其他表)的聚集
- 事实表一般是反应了用户某种行为表
比如说:
- 订单表, 收藏表, 登录表, 购物车表 …
事实表分类: - 事务事实表 : 最初始确定的事实表 其实就是事务事实表
- 累计快照事实表: 指的对数据进行提前聚合后表, 比如将事实表按照天聚合统计 结果表
- 周期快照事实表: 每一条数据, 记录了完整的事件 从开始 到结束整个流程, 一般有多个时间组成
3.4维度表
维度表: 当对事实表进行统计分析的时候, 可能需要关联一些其他表进行辅助, 这些表其实就是维度表
- 维度表一般是由平台或者商家来构建的表, 与用户无关, 不会反应用户的行为
- 比如说: 地区表 商品表 时间表, 分类表…
维度表分类:
高基数维度表: 如果数据量达到几万 或者几十万 甚至几百万的数据量, 一般这样维度表称为高基数维度表
- 比如: 商品表 , 用户表
低基数维度表: 如果数据量只有几条 或者 几十条 或者几千条, 这样称为低基数维度表
- 比如: 地区表 时间表 分类表 配置表
3.5数仓发展模型
数仓发展模型的三种模型
星型模型:
- 特点: 只有一个事实表, 也就意味着只有一个分析的主题, 在事实表周围围绕了多张维度表, 维度表与维度表没有任何关联
- 数仓发展阶段: 初期
雪花模型:
- 特点: 只有一个事实表, 也就意味着只有一个分析的主题, 在事实表周围围绕了多张维度表, 维度表可以接着关联其他的维度表
- 数仓发展阶段: 异常, 出现畸形状态 在实际数仓中, 这种模型建议越少越好, 尽量避免这种模型产生
星座模型:
- 特点: 有多个事实表, 也就意味着有了多个分析的主题, 在事实表周围围绕了多张维度表, 在条件吻合的情况下, 事实表之间是可以共享维度表
- 数仓发展阶段: 中期 和 后期
3.6缓慢渐变维
主要是用于解决历史变更问题, 处理历史变更的数据是否需要保留的问题
如何解决问题?
- SCD1: 不维护历史变更行为, 直接对过去数据进行覆盖即可(此种操作 仅适用于错误数据的处理)
- SCD2: 维护历史变更行为, 处理方式 在表中新增两个新的字段, 一个是起始时间, 一个是结束时间, 当数据发生变更后,将之前的数据设置为过期, 将新的变更后完整的数据添加到表中, 重新记录其起始和结束时间, 将这种方案称为 **拉链表(好处: 可以维护更多的历史版本的数据, 处理起来也是比较简单的 (利于维护)),(弊端: 造成数据冗余存储 大量占用磁盘空间)
- SCD3: 维度历史变化, 处理方式, 当表中有字段发生变更后, 新增一列, 将变更后的数据存储到这一列中即可(好处:
减少数据冗余存储),(弊端: 只能维护少量的历史版本, 而且维护不方便, 效率比较低),(适用于:
空间比较紧缺,而且只需要维护少量版本的情况)
3.7数仓分层介绍
ODS: 源数据层(临时存储层)
- 作用: 对接数据源, 用于将数据源的数据完整的导入到ODS层中, 一般ODS层的数据和数据源的数据保持一致, 类似于一种数据迁移的操作,
一般在ODS层建表的时候, 会额外增加一个 日期的分区, 用于标记何时进行数据采集
DW: 数据仓库层
- 作用: 用于进行数据统计分析的操作, 数据来源于 ODS层
APP(DA|ADS | RPT |ST) : 数据应用层(数据展示层)
- 作用: 存储分析的结果信息, 用于对接相关的应用, 比如 BI图表
以新零售项目为例:
ODS层: 源数据层(临时存储层) 作用: 对接数据源, 用于将数据源的数据完整的导入到ODS层中,一般ODS层的数据和数据源的数据保持一致, 类似于一种数据迁移的操作, 一般在ODS层建表的时候, 会额外增加一个 日期的分区, 用于标记何时进行数据采集
DW层: 数据仓库层
DWD层: 明细层
作用: 和ODS层保持相同的粒度,不会对数据进行聚合操作, 只要进行清洗 转换工作, 保证数据质量,利于后续分析
清洗: 过滤掉一些无用数据
转换: 格式转换 或者 一个字段 转换为多个字段, json转换....
DWB层: 基础数据层
作用: 进行维度退化的操作, 根据业务模块. 形成业务宽表
维度退化:
以订单表为例, 在订单表中, 有用户 id , 商品id 商家id, 地区的id信息. 如果统计需要按照用户, 商品, 商家 和地区来统计操作, 此时需要关联用户表, 商品表 商家表 地区表
维度退化:
提前先将这些维度表中可能需要使用字段合并到事实表中, 让事实表变的更宽,后续在统计的时候, 只需要关联订单表即可
DWS层: 业务数据层
作用: 用于进行提前聚合操作, 形成基础主题统计宽表指标数据
例如: 需求要求统计 每年 每月 每日的销售额. 那么在DWS层, 可以先按照日形成统计结果数据
DM层: 数据集市层
作用: 基于主题, 形成数据集市, 对指标进行细化统计过程
例如: 需要将 每年 每月 每日的销售额全部记录在DM层中, 此时我们只需要对DWS层进行上卷统计即可
APP(DA|ADS | RPT |ST) : 数据应用层(数据展示层)
作用: 存储分析的结果信息, 用于对接相关的应用, 比如 BI图表
4.数仓工具的基本使用
4.1使用 HUE 操作 oozie
oozie:是一个工作流读调度工具,实现对工作流的定时调度操作
工作流:指业务过程的部分或整体在计算机应用环境下的自动化
工作流一般要满足以下几个特征:
1- 一个流程是可以被拆解为多个阶段(步骤)
2- 多个阶段之间存在依赖关系, 前序没有执行, 后续无法执行
3- 整个流程 需要周而复始不断的干
请问, 大数据的工作流程是否可以使用工作流解决呢?
先回答: 大数据的工作流程有那些呢?
1- 确定数据源
2- 数据的存储
3- 数据的预处理
4- 数据的分析处理
5- 数据的应用
所以说大数据可以使用工作流解决问题的
能够实现工作流的大数据组件有那些?
oozie:
是apache旗下一款工作流的调度工具, 出现时间也是比较久的, oozie如果单独使用, 是非常麻烦的, 提供的管理界面仅仅只能查看一些状态, 无法对工作流进行操作, 所有的操作都需要通过配置 XML文档, 整个配置非常复杂的
但是由于人家apache 大数据 全家桶一员, HUE在集成一款调度工具, 优先选择自家产品, 使用HUE, 只需要用户通过鼠标点一点即可完成oozie工作流的配置了
azkaban:
属于领英公司旗下一款的工作流的调度工具, 开源免费的, azkaban简单来说就是一个shell脚本调度工具, 提供了同一个工作流的界面,可以直接在界面上提交工作流 ,并对工作流进行监控, 整个工作流的配置, 仅需要简单配置几行类似于properties文件即可完成
单独使用角度: azkaban
如果结合HUE: oozie
4.2如何使用oozie
1.开启hue对oozie的支持
- 2- 重启 HUE , 实现配置生效
3.查看hue界面
查看输出结果:
4.如何配置定时:
4.3sqoop的基本使用操作
4.3.1sqoop的概述
1.sqoop是apache旗下顶级项目. 主要是用于 RDBMS 和 大数据生态圈之间的数据导入导出的工具, 从RDBMS 到大数据生态圈 是导入操作, 反之为导出操作
sqoop本质上也是一款翻译软件, 将sqoop的命令翻译为 MR程序
关于使用sqoop 将数据导入到HIVE, 支持两种导入方案: 原生导入方案 和 hcatalog方式
区别点:
如果使用原生导入方式, 导入HIVE , 仅支持 textFile导入方式
hcatalog支持数据存储方案比较多: textFile, ORC, sequence, parquet....
原生方式支持数据覆盖导入
hcatalog仅支持追加导入
原生方式在导入时候, 根据字段的顺序, 导入到HIVE中
hcatalog在导入的时候, 是根据字段的名称导入的
(此部分建议在导入到HIVE , hive表字段的顺序 和 mysql表字段顺序保持一致, 名称保持一致)
后续主要采用 hcatalog的导入方式, 因为建表的时候, 主要存储格式为 ORC
4.3.2基本操作
(1).如何查看某个操作下的相关的参数信息:
sqoop 操作 --help
(2)查询mysql中所有的库有那些?
思考: 连接mysql需要知道什么信息呢?
1- 用户名 2- 密码 3- 连接地址
sqoop list-databases --connect jdbc:mysql://hadoop01:3306 --username root --password 123456
(3)查询mysql中scm中所有的表
sqoop list-tables \
--connect jdbc:mysql://hadoop01:3306/scm \
--username root \
--password 123456
4.3.3数据全量导入操作
创建表:
create database test default character set utf8mb4 collate utf8mb4_unicode_ci;
use test;
create table emp
(
id int not null
primary key,
name varchar(32) null,
deg varchar(32) null,
salary int null,
dept varchar(32) null
);
INSERT INTO emp (id, name, deg, salary, dept) VALUES (1201, 'gopal', 'manager', 50000, 'TP');
INSERT INTO emp (id, name, deg, salary, dept) VALUES (1202, 'manisha', 'Proof reader', 50000, 'TP');
INSERT INTO emp (id, name, deg, salary, dept) VALUES (1203, 'khalil', 'php dev', 30000, 'AC');
INSERT INTO emp (id, name, deg, salary, dept) VALUES (1204, 'prasanth', 'php dev', 30000, 'AC');
INSERT INTO emp (id, name, deg, salary, dept) VALUES (1205, 'kranthi', 'admin', 20000, 'TP');
create table emp_add
(
id int not null
primary key,
hno varchar(32) null,
street varchar(32) null,
city varchar(32) null
);
INSERT INTO emp_add (id, hno, street, city) VALUES (1201, '288A', 'vgiri', 'jublee');
INSERT INTO emp_add (id, hno, street, city) VALUES (1202, '108I', 'aoc', 'sec-bad');
INSERT INTO emp_add (id, hno, street, city) VALUES (1203, '144Z', 'pgutta', 'hyd');
INSERT INTO emp_add (id, hno, street, city) VALUES (1204, '78B', 'old city', 'sec-bad');
INSERT INTO emp_add (id, hno, street, city) VALUES (1205, '720X', 'hitec', 'sec-bad');
create table emp_conn
(
id int not null
primary key,
phno varchar(32) null,
email varchar(32) null
);
INSERT INTO emp_conn (id, phno, email) VALUES (1201, '2356742', '[email protected]');
INSERT INTO emp_conn (id, phno, email) VALUES (1202, '1661663', '[email protected]');
INSERT INTO emp_conn (id, phno, email) VALUES (1203, '8887776', '[email protected]');
INSERT INTO emp_conn (id, phno, email) VALUES (1204, '9988774', '[email protected]');
INSERT INTO emp_conn (id, phno, email) VALUES (1205, '1231231', '[email protected]');
如何全量将数据导入到HDFS中:
需求一: 将 emp表中数据导入到HDFS中
需要知道什么信息? 数据库的基本信息(连接地址, 用户名, 密码, 表名) 目的地的路径信息
方式一:
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp
说明:
1- 当不指定导出路径的时候, 默认会将数据导入到当前操作用户的HDFS的家目录下, 再次目录下以表名创建一个文件夹, 将数据放置到这个文件夹中
2- 发现在导入数据的时候, 有多少条数据, 就会运行多少个mapTask, 最高和cpu核数相等
3- 数据之间的分隔符号为 逗号
思考: 是否可以将其导入到其他位置呢? --target-dir 和 --delete-target-dir
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--delete-target-dir \
--target-dir /sqoop_works/emp
说明:
--target-dir : 将数据导入到HDFS的那个位置中
--delete-target-dir: 如果目的地路径以存在, 先删除
思考: 是否可以设置其mapTask的数量呢? -m 和 --split-by
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--delete-target-dir \
--target-dir /sqoop_works/emp \
-m 2 \
--split-by id
说明:
如果 -m为1 , 表示只允许一个mapTask, 此时可能省略 --split-by
--split-by 表示按照那个字段进行切割数据表, 一般设置为主键字段, 如果主键字段是多个, 那么就写多个, 用逗号隔开
思考: 是否可以调整分隔符号呢? 比如说 设置为 | 参数: --fields-terminated-by
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--delete-target-dir \
--target-dir /sqoop_works/emp \
--fields-terminated-by '|' \
-m 1
如何全量导入数据到HIVE中
需求:将emp_add这个表的全部数据导入到HIVE的对一个表中
# 1- 在HIVE中创建目标表
create database if not exists day03_xls;
create table if not exists day03_xls.emp_add(
id int,
hno string,
street string,
city string
)
row format delimited fields terminated by '\t' stored as orc;
# 2- 编写 sqoop命令 完成数据导入操作
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp_add \
--fields-terminated-by '\t' \
--hcatalog-database 'day03_xls' \
--hcatalog-table 'emp_add' \
-m 1
注意:
由于 hive表的存储格式为 orc, 所以无法使用sqoop的原生导入方案, 必须使用hcatalog
4.3.4数据条件导入数据
方式一: 通过 where条件的方式, 将部分数据导入到HDFS中
# 1- 以 emp为例, 在这个表中, 新增一条数据, 尝试将这一条数据导入到HDFS中
INSERT INTO test.emp VALUES(1206,'zhangsan','bigdata dev','30000','TP');
# 2- 通过 sqoop实现数据条件导入
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--where 'id > 1205' \
--delete-target-dir \
--target-dir /sqoop_works/emp_1 \
--fields-terminated-by '|' \
-m 1
方式二: 通过 SQL的方式, 将部分数据导入到HDFS中:
# 1- 以 emp为例, 在这个表中, 新增一条数据, 尝试将这一条数据导入到HDFS中
INSERT INTO test.emp VALUES(1207,'lisi','bigdata dev','50000','TP');
# 2- 使用sqoop条件导入数据: 采用 SQL形式
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--query 'select * from emp where id > 1206 and $CONDITIONS' \
--delete-target-dir \
--target-dir /sqoop_works/emp_2 \
--fields-terminated-by '|' \
-m 1
注意:
1- 当使用 --query 方式的时候, 不允许在使用 --table, 因为 SQL中已经明确需要导入那个表的数据
2- 当使用 --query 方式的时候, 编写的SQL语句必须添加where条件, 条件最后必须要跟 $CONDITIONS, 如果使用双引号包裹SQL, $前面必须加一个 \ 进行转义操作, 当如果没有条件的时候,建议写成: where 1=1 and $CONDITIONS
4.3.5数据全量导出操作
需求: 将HIVE中 emp_add表中所有的数据全量导出MySQL中
步骤一: 在MySQL中创建目标表
CREATE TABLE test.emp_add_mysql (
id INT,
hno VARCHAR(20),
street VARCHAR(20),
city VARCHAR(20)
);
步骤二: 执行sqoop导出操作
sqoop export \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp_add_mysql \
--fields-terminated-by '\t' \
--hcatalog-database 'day03_xls' \
--hcatalog-table 'emp_add' \
-m 1
注意:
在执行数据导出的时候, 必须先创建目标表, 然后才能触发执行导出
4.3.6安装DBeaver
2.设置环境变量
3.连接mysql
4.导入业务端数据
5或者使用命令导入
source sqo脚本
5.hive的基础优化
5.1HDFS的副本数量的调整
一般HDFS的副本数量设置为3, 如果数据不是特别重要建议设置为2
注意: 在HDFS的3.0版本后, 起始可以调整为1.5 (1个副本 + 纠删码(占用副本50%大小))
5.2nodemanager的相关基础配置
CPU配置操作:
-
配置项:yarn.nodemanager.resource.cpu-vcores (直接在CM上修改YARN配置即可)
-
默认值为: 8 yarn不会自动检测服务器的CPU核心数, 一般都需要手动调整
-
推荐配置: 对应服务器是多少核 就配置多少核
查看服务器CPU的核心数: 方式一: 命令行方式 grep 'processor' /proc/cpuinfo | sort -u | wc -l 方式二: 直接通过CM查询
如何配置:直接在CM上进行调整即可
内存配置
配置项
-
yarn.nodemanager.resource.memory-mb (直接在CM上进行配置即可)
-
yarn.scheduler.maximum-allocation-mb :调度器内存配置, 和第一个保持一致
-
yarn.app.mapreduce.am.command-opts : 略小于第一个配置内存大小
-
默认值: 8GB yarn不会自动检测服务器的内存值, 一般都需要手动调整
-
推荐配置: 剩余内存 * 0.8 左右
-
如何配置: 直接在CM上配置即可
本地目录的配置 -
配置项:yarn.nodemanager.local-dirs(Yarn)
-
说明: 在执行MR过程中, nodemanager产生临时文件存储在那些目录中
-
推荐配置: 服务器挂载了几个磁盘, 就配置几个目录 一个目录对应其中一个磁盘位置
磁盘挂载目录的查询命令: df -lh
5.3MapReduce的内存配置
相关的内存配置内容(一般不配置,正常使用即可,如果发现执行效率非常慢,可以尝试调整内存配置)
mapreduce.map.memory.mb: 用于配置map的内存大小
mapreduce.reduce.memory.mb: 用于配置reduce的内存大小
mapreduce.map.java.opts : 用于配置map的jvm的内存大小
mapreduce.reduce.java.opts: 用于配置reduce的jvm的内存大小
说明:
1. 对应jvm的内存配置要略小于 map或者reduce的内存大小配置
2. map和reduce的内存配置大小, 不能超过nodemanager的内存大小
5.4hive的基础配置
1.hiveserver2的java堆栈的内存配置
配置项:hiveserver2的堆栈大小
此配置后续会有一个报错,反应就是此配置过小
此操作在遇到问题,在进行调整,此时暂时不调整
HIve并行编译
默认情况下,hive同时只能编译一段hive sql并上锁
配置项:hive.driver.parallel.compilation
- 生成动态分区的线程数量:
- 配置项: hive.load.dynamic.partitions.thread (直接在CM上调整)
- 默认值: 15
- 此配置, 值越大, 执行效率越高, 但是资源损耗也会越高
- 监听输入的线程数量:
- 配置项: hive.exec.input.listing.max.threads
- 默认值: 15
- 此配置, 值越大, 读取效率越高, 但是资源损耗也会越高
5.5压缩的配置
HIVE 本质上, 将SQL翻译为MR , 所以hive压缩配置, 更多指的是对MR的压缩配置操作
关于压缩的相关的配置:
set hive.exec.compress.intermediate=true; 是否开启hive的支持中间结果压缩配置 默认关闭的
mapreduce.map.output.compress : 是否开启map端压缩配置 默认开启的
mapreduce.map.output.compress.codec: map端采用何种压缩方案
选择为:
org.apache.hadoop.io.compress.SnappyCodec
set hive.exec.compress.output=true; 是否开启hive的最终结果的压缩配置 默认是关闭的]
mapreduce.output.fileoutputformat.compress: 是否开启reduce端压缩配置 默认关闭的
mapreduce.output.fileoutputformat.compress.codec : reduce端采用何种压缩方案
选择为:
org.apache.hadoop.io.compress.SnappyCodec
mapreduce.output.fileoutputformat.compress.type : 配置压缩方案
选择为: BLOCK(块压缩)
注意:
只要不是带有 set操作的配置, 都是直接在cm上进行配置操作
带有set的 是需要在客户端中进行配置的
5.6执行引擎选择
配置项: hive.execution.engine
6.ODS数据采集操作
6.1ODS数据采集概述
作用: 对接数据源, 一般和数据源保持相同的粒度
ODS层: 处于在HIVE端
业务数据: MySQL
目标: 将MySQL中业务库的表数据 导入到 ODS层中
技术: sqoop 完成导入的操作
6.2数据的存储格式和压缩方案
1.存储格式两大类:行式存储和列式存储
行式存储优点(textFIle):可读性较好,执行select效率较高。缺点:耗费磁盘资源,执行select字段,效率较低
列式存储优点(orc):节省磁盘空间,执行select字段,效率比较高。缺点:执行select效率比较低,可读性不是很好。
orc是兼具行式存储优势和列式存储优势,数据按行分块,每块中按列存储数据,同时在每个块内部,对数据构建索引,提升查询的效率.
在hive中,一般我们的选择都是orc存储格式,除非需求对接的数据源是普通文本文件数据,此时会对接此文件的表构建为textfile,其余的层次结构的表依然使用orc.
2压缩方案:
zlib(gzip):具有良好的压缩比,但是解压缩的性能一般
snappy:具有良好的解压缩的性能,同时具有较好的压缩比,弊端,没有zlib压缩比好,同时hadoop默认原生是不支持snappy压缩的(chd版本直接支持的)
本项目主要采用snappy的压缩方式:
在ods层,一般会使用zlib
在其他层次中,一般采用snappy
使用说明:
如果读取此时比较少,写入了较大,优先保证压缩比,比如说ods层,如果读取次数比较高,优先保障解压缩性能,如果不清楚,建议使用snappy,或者如果空间足够,统一采用sanppy也没有问题
3.创建表选择内部表,还是外部表
判断标准:对数据是否有管理的权限
有权限删除数据,那么我们可以构建内部表,当然也可以构建外部表,如果没有权限删除数据,只能构建外部表.
内部表转换为外部表:
alter table 表名 set tblproperties('EXTERANL'='FALSE');
通过true和false来修改是否为内部表还是外部表
一般来说,在数仓中,除了ODS层可能会出现外部表以外,其余的层次结构,大多数还是外部表.
3.创建表的时候,是否需要构建分区表呢
一般情况下,都是分区表(分区字段大多数都是以时间为主)
6.3数据同步方式
1.全量覆盖同步方式
适用于表数据变更的二频次并不多,不需要记录其历史数据,而且整个表数据量相对比较少,这个时候可以采用全量覆盖的操作.
操作方式:
每次同步数据,都是要先将原有的数仓中标数据全部删除,然后重新从业务端导入即可,建表的时候,不需要构建分区表.
比如说:地区表,时间表
2.仅新增同步方式
适用于业务端数据只会有新增的操作,不会有变更的时候,数据量比较多.
操作方式:
在数仓中建表的时候,需要构建分区表,分区字段和同步数据的周期是一致,每次进行同步的时候,将对应周期下的新增数据放置到对应日期分区下
比如说:登记日志表,访问日志表
3.新增及更新同步方式
适用于业务端数据既有更新操作,又有新增操作的时候,而且数据量比较多
操作方式:
在数仓中建表的时候,需要构建分区表,分区字段和同步数据的周期是一致的.每次进行同步的时候,将对应这个周期的新增数据和更新数据放置到对应日期分区下即可.
比如说:
订单表,商品表,用户表....
4.全量同步方式
适用于业务端数据量不是特别大,但是也存在更新和新增,而且不需要保留太多的历史版本
操作方式:
在数仓中建表的时候,需要构建分区表,分区字段和同步数据的周期是一致的,比如说: 每天都需要同步数据, 分区字段 需要按天 如果每月同步一次, 分区字段按照月,每次导入都是导入截止当前时间的全量数据, 定期将历史的日期数据删除即可
6.4中文乱码的问题
解决方案:
在mysql的hive库中, 执行一下SQL即可:
use hive;
alter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;
alter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;
alter table PARTITION_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8 ;
alter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;
alter table INDEX_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;
6.5创建ODS层相关的表
1.创建库
drop database if exists yp_ods;
create database if not exists yp_ods;
2.构建ods层表
建表说明:
ODS层表与 业务库的表保持一致, 也就说, 业务库中有哪些表, 那么我们就需要在ODS层构建有那些表, 表中到的字段也要一致, 额外在HIVE建表的时候, 需要多加一个分区字段, 用于标记数据在何时导入进来的.
然后判断, 那些表是属于全量覆盖的表, 那些表是属于仅新增的表, 那些表是属于全量及更新的表, 基于各个不同同步方式, 构建对应表即可.
6.6全量覆盖表
-- 区域表: t_district
DROP TABLE if exists yp_ods.t_district;
CREATE TABLE yp_ods.t_district
(
id string COMMENT '主键ID',
code string COMMENT '区域编码',
name string COMMENT '区域名称',
pid int COMMENT '父级ID',
alias string COMMENT '别名'
)
comment '区域字典表'
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 日期表: t_date
drop table yp_ods.t_date;
CREATE TABLE yp_ods.t_date (
dim_date_id string COMMENT '日期',
date_code string COMMENT '日期编码',
lunar_calendar string COMMENT '农历',
year_code string COMMENT '年code',
year_name string COMMENT '年名称',
month_code string COMMENT '月份编码',
month_name string COMMENT '月份名称',
quanter_code string COMMENT '季度编码',
quanter_name string COMMENT '季度名称',
year_month string COMMENT '年月',
year_week_code string COMMENT '一年中第几周',
year_week_name string COMMENT '一年中第几周名称',
year_week_code_cn string COMMENT '一年中第几周(中国)',
year_week_name_cn string COMMENT '一年中第几周名称(中国',
week_day_code string COMMENT '周几code',
week_day_name string COMMENT '周几名称',
day_week string COMMENT '周',
day_week_cn string COMMENT '周(中国)',
day_week_num string COMMENT '一周第几天',
day_week_num_cn string COMMENT '一周第几天(中国)',
day_month_num string COMMENT '一月第几天',
day_year_num string COMMENT '一年第几天',
date_id_wow string COMMENT '与本周环比的上周日期',
date_id_mom string COMMENT '与本月环比的上月日期',
date_id_wyw string COMMENT '与本周同比的上年日期',
date_id_mym string COMMENT '与本月同比的上年日期',
first_date_id_month string COMMENT '本月第一天日期',
last_date_id_month string COMMENT '本月最后一天日期',
half_year_code string COMMENT '半年code',
half_year_name string COMMENT '半年名称',
season_code string COMMENT '季节编码',
season_name string COMMENT '季节名称',
is_weekend string COMMENT '是否周末(周六和周日)',
official_holiday_code string COMMENT '法定节假日编码',
official_holiday_name string COMMENT '法定节假日',
festival_code string COMMENT '节日编码',
festival_name string COMMENT '节日',
custom_festival_code string COMMENT '自定义节日编码',
custom_festival_name string COMMENT '自定义节日',
update_time string COMMENT '更新时间'
)
COMMENT '时间维度表'
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
6.7仅新增同步表
-- 订单评价表 : t_goods_evaluation
DROP TABLE if exists yp_ods.t_goods_evaluation;
CREATE TABLE yp_ods. (
`id` string,
`user_id` string COMMENT '评论人id',
`store_id` string COMMENT '店铺id',
`order_id` string COMMENT '订单id',
`geval_scores` INT COMMENT '综合评分',
`geval_scores_speed` INT COMMENT '送货速度评分0-5分(配送评分)',
`geval_scores_service` INT COMMENT '服务评分0-5分',
`geval_isanony` TINYINT COMMENT '0-匿名评价,1-非匿名',
`create_user` string,
`create_time` string,
`update_user` string,
`update_time` string,
`is_valid` TINYINT COMMENT '0 :失效,1 :开启'
)
comment '商品评价表'
partitioned by (dt string)
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 登录记录表: t_user_login
DROP TABLE if exists yp_ods.t_user_login;
CREATE TABLE yp_ods.t_user_login(
id string,
login_user string,
login_type string COMMENT '登录类型(登陆时使用)',
client_id string COMMENT '推送标示id(登录、第三方登录、注册、支付回调、给用户推送消息时使用)',
login_time string,
login_ip string,
logout_time string
)
COMMENT '用户登录记录表'
partitioned by (dt string)
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 订单组支付表: t_order_pay
DROP TABLE if exists yp_ods.t_order_pay;
CREATE TABLE yp_ods.t_order_pay (
id string,
group_id string COMMENT '关联shop_order_group的group_id,一对多订单',
order_pay_amount DECIMAL(11,2) COMMENT '订单总金额;',
create_date string COMMENT '订单创建的时间,需要根据订单创建时间进行判断订单是否已经失效',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '订单支付表'
partitioned by (dt string)
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
6.8新增及更新同步表
-- 商品评价明细表 t_goods_evaluation_detail
DROP TABLE if exists yp_ods.t_goods_evaluation_detail;
CREATE TABLE yp_ods.t_goods_evaluation_detail
(
id string,
user_id string COMMENT '评论人id',
store_id string COMMENT '店铺id',
goods_id string COMMENT '商品id',
order_id string COMMENT '订单id',
order_goods_id string COMMENT '订单商品表id',
geval_scores_goods INT COMMENT '商品评分0-10分',
geval_content string,
geval_content_superaddition string COMMENT '追加评论',
geval_addtime string COMMENT '评论时间',
geval_addtime_superaddition string COMMENT '追加评论时间',
geval_state TINYINT COMMENT '评价状态 1-正常 0-禁止显示',
geval_remark string COMMENT '管理员对评价的处理备注',
revert_state TINYINT COMMENT '回复状态0未回复1已回复',
geval_explain string COMMENT '管理员回复内容',
geval_explain_superaddition string COMMENT '管理员追加回复内容',
geval_explaintime string COMMENT '管理员回复时间',
geval_explaintime_superaddition string COMMENT '管理员追加回复时间',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '商品评价明细'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 订单配送详细信息表 : t_order_delievery_item
DROP TABLE if exists yp_ods.t_order_delievery_item;
CREATE TABLE yp_ods.t_order_delievery_item
(
id string COMMENT '主键id',
shop_order_id string COMMENT '订单表ID',
refund_order_id string,
dispatcher_order_type TINYINT COMMENT '配送订单类型1.支付单; 2.退款单',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
circle_master_user_id string COMMENT '圈主ID',
dispatcher_user_id string COMMENT '配送员ID',
dispatcher_order_state TINYINT COMMENT '配送订单状态:0.待接单.1.已接单,2.已到店.3.配送中 4.商家普通提货码完成订单.5.商家万能提货码完成订单。6,买家完成订单',
order_goods_num TINYINT COMMENT '订单商品的个数',
delivery_fee DECIMAL(11,2) COMMENT '配送员的运费',
distance INT COMMENT '配送距离',
dispatcher_code string COMMENT '收货码',
receiver_name string COMMENT '收货人姓名',
receiver_phone string COMMENT '收货人电话',
sender_name string COMMENT '发货人姓名',
sender_phone string COMMENT '发货人电话',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true'
)
comment '订单配送详细信息表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 所有交易记录信息 t_trade_record
DROP TABLE if exists yp_ods.t_trade_record;
CREATE TABLE yp_ods.t_trade_record
(
id string COMMENT '交易单号',
external_trade_no string COMMENT '(支付,结算.退款)第三方交易单号',
relation_id string COMMENT '关联单号',
trade_type TINYINT COMMENT '1.支付订单; 2.结算订单; 3.退款订单;4.充值单;5.提现单;6.分销单;7缴纳保证金单8退还保证金单9,冻结通联订单,10通联通账户余额充值,11.扫码单',
status TINYINT COMMENT '1.成功;2.失败;3.进行中',
finnshed_time string COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
fail_reason string COMMENT '交易失败的原因',
payment_type string COMMENT '支付方式:小程序,app微信,支付宝,快捷支付,钱包,银行卡,消费券',
trade_before_balance DECIMAL(11,2) COMMENT '交易前余额',
trade_true_amount DECIMAL(11,2) COMMENT '交易实际支付金额,第三方平台扣除优惠以后实际支付金额',
trade_after_balance DECIMAL(11,2) COMMENT '交易后余额',
note string COMMENT '业务说明',
user_card string COMMENT '第三方平台账户标识/多钱包用户钱包id',
user_id string COMMENT '用户id',
aip_user_id string COMMENT '钱包id',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '所有交易记录信息'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 商圈表: t_trade_area
DROP TABLE if exists yp_ods.t_trade_area;
CREATE TABLE yp_ods.t_trade_area
(
id string COMMENT '主键',
user_id string COMMENT '用户ID',
user_allinpay_id string COMMENT '通联用户表id',
trade_avatar string COMMENT '商圈logo',
name string COMMENT '商圈名称',
notice string COMMENT '商圈公告',
distric_province_id INT COMMENT '商圈所在省份ID',
distric_city_id INT COMMENT '商圈所在城市ID',
distric_area_id INT COMMENT '商圈所在县ID',
address string COMMENT '商圈地址',
radius double COMMENT '半径',
mb_title_img string COMMENT '手机商圈 页头背景图',
deposit_amount DECIMAL(11,2) COMMENT '商圈认购费用总额',
hava_deposit INT COMMENT '是否有交过保证金 1:是0:否',
state TINYINT COMMENT '申请商圈状态 -1 :未认购 ;0 :申请中;1 :已认购 ;',
search_key string COMMENT '商圈搜索关键字',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true'
)
comment '商圈表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 地址信息 : t_location
DROP TABLE if exists yp_ods.t_location;
CREATE TABLE yp_ods.t_location
(
id string COMMENT '主键',
type INT COMMENT '类型 1:商圈地址;2:店铺地址;3.用户地址管理;4.订单买家地址信息;5.订单卖家地址信息',
correlation_id string COMMENT '关联表id',
address string COMMENT '地图地址详情',
latitude double COMMENT '纬度',
longitude double COMMENT '经度',
street_number string COMMENT '门牌',
street string COMMENT '街道',
district string COMMENT '区县',
city string COMMENT '城市',
province string COMMENT '省份',
business string COMMENT '百度商圈字段,代表此点所属的商圈',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true',
adcode string COMMENT '百度adcode,对应区县code'
)
comment '地址信息'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 商品表_店铺(SKU)
DROP TABLE if exists yp_ods.t_goods;
CREATE TABLE yp_ods.t_goods
(
id string,
store_id string COMMENT '所属商店ID',
class_id string COMMENT '分类id:只保存最后一层分类id',
store_class_id string COMMENT '店铺分类id',
brand_id string COMMENT '品牌id',
goods_name string COMMENT '商品名称',
goods_specification string COMMENT '商品规格',
search_name string COMMENT '模糊搜索名称字段:名称_+真实名称',
goods_sort INT COMMENT '商品排序',
goods_market_price DECIMAL(11,2) COMMENT '商品市场价',
goods_price DECIMAL(11,2) COMMENT '商品销售价格(原价)',
goods_promotion_price DECIMAL(11,2) COMMENT '商品促销价格(售价)',
goods_storage INT COMMENT '商品库存',
goods_limit_num INT COMMENT '购买限制数量',
goods_unit string COMMENT '计量单位',
goods_state TINYINT COMMENT '商品状态 1正常,2下架,3违规(禁售)',
goods_verify TINYINT COMMENT '商品审核状态: 1通过,2未通过,3审核中',
activity_type TINYINT COMMENT '活动类型:0无活动1促销2秒杀3折扣',
discount INT COMMENT '商品折扣(%)',
seckill_begin_time string COMMENT '秒杀开始时间',
seckill_end_time string COMMENT '秒杀结束时间',
seckill_total_pay_num INT COMMENT '已秒杀数量',
seckill_total_num INT COMMENT '秒杀总数限制',
seckill_price DECIMAL(11,2) COMMENT '秒杀价格',
top_it TINYINT COMMENT '商品置顶:1-是,0-否',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '商品表_店铺(SKU)'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 商品分类_店铺
DROP TABLE if exists yp_ods.t_goods_class;
CREATE TABLE yp_ods.t_goods_class
(
id string,
store_id string COMMENT '店铺id',
class_id string COMMENT '对应的平台分类表id',
name string COMMENT '店铺内分类名字',
parent_id string COMMENT '父id',
level TINYINT COMMENT '分类层级',
is_parent_node TINYINT COMMENT '是否为父节点:1是0否',
background_img string COMMENT '背景图片',
img string COMMENT '分类图片',
keywords string COMMENT '关键词',
title string COMMENT '搜索标题',
sort INT COMMENT '排序',
note string COMMENT '类型描述',
url string COMMENT '分类的链接',
is_use TINYINT COMMENT '是否使用:0否,1是',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '商品分类_店铺'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 品牌(店铺)
DROP TABLE if exists yp_ods.t_brand;
CREATE TABLE yp_ods.t_brand
(
id string,
store_id string COMMENT '店铺id',
brand_pt_id string COMMENT '平台品牌库品牌Id',
brand_name string COMMENT '品牌名称',
brand_image string COMMENT '品牌图片',
initial string COMMENT '品牌首字母',
sort INT COMMENT '排序',
is_use TINYINT COMMENT '0禁用1启用',
goods_state TINYINT COMMENT '商品品牌审核状态 1 审核中,2 通过,3 拒绝',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '品牌(店铺)'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 订单表
DROP TABLE if exists yp_ods.t_shop_order;
CREATE TABLE yp_ods.t_shop_order
(
id string COMMENT '根据一定规则生成的订单编号',
order_num string COMMENT '订单序号',
buyer_id string COMMENT '买家的userId',
store_id string COMMENT '店铺的id',
order_from TINYINT COMMENT '是来自于app还是小程序,或者pc 1.安卓; 2.ios; 3.小程序H5 ; 4.PC',
order_state INT COMMENT '订单状态:1.已下单; 2.已付款, 3. 已确认 ;4.配送; 5.已完成; 6.退款;7.已取消',
create_date string COMMENT '下单时间',
finnshed_time timestamp COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
is_settlement TINYINT COMMENT '是否结算;0.待结算订单; 1.已结算订单;',
is_delete TINYINT COMMENT '订单评价的状态:0.未删除; 1.已删除;(默认0)',
evaluation_state TINYINT COMMENT '订单评价的状态:0.未评价; 1.已评价;(默认0)',
way string COMMENT '取货方式:SELF自提;SHOP店铺负责配送',
is_stock_up INT COMMENT '是否需要备货 0:不需要 1:需要 2:平台确认备货 3:已完成备货 4平台已经将货物送至店铺 ',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '订单表'
partitioned by (dt string)
row format delimited fields terminated by '\t'
stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 订单详情表
DROP TABLE if exists yp_ods.t_shop_order_address_detail;
CREATE TABLE yp_ods.t_shop_order_address_detail
(
id string COMMENT '关联订单的id',
order_amount DECIMAL(11,2) COMMENT '订单总金额:购买总金额-优惠金额',
discount_amount DECIMAL(11,2) COMMENT '优惠金额',
goods_amount DECIMAL(11,2) COMMENT '用户购买的商品的总金额+运费',
is_delivery string COMMENT '0.自提;1.配送',
buyer_notes string COMMENT '买家备注留言',
pay_time string,
receive_time string,
delivery_begin_time string,
arrive_store_time string,
arrive_time string COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '订单详情表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 订单结算表
DROP TABLE if exists yp_ods.t_order_settle;
CREATE TABLE yp_ods.t_order_settle
(
id string COMMENT '结算单号',
order_id string,
settlement_create_date string COMMENT '用户申请结算的时间',
settlement_amount DECIMAL(11,2) COMMENT '如果发生退款,则结算的金额 = 订单的总金额 - 退款的金额',
dispatcher_user_id string COMMENT '配送员id',
dispatcher_money DECIMAL(11,2) COMMENT '配送员的配送费(配送员的运费(如果退货方式为1:则买家支付配送费))',
circle_master_user_id string COMMENT '圈主id',
circle_master_money DECIMAL(11,2) COMMENT '圈主分润的金额',
plat_fee DECIMAL(11,2) COMMENT '平台应得的分润',
store_money DECIMAL(11,2) COMMENT '商家应得的订单金额',
status TINYINT COMMENT '0.待结算;1.待审核 ; 2.完成结算;3.拒绝结算',
note string COMMENT '原因',
settle_time string COMMENT ' 结算时间',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志',
first_commission_user_id string COMMENT '一级分佣用户',
first_commission_money DECIMAL(11,2) COMMENT '一级分佣金额',
second_commission_user_id string COMMENT '二级分佣用户',
second_commission_money DECIMAL(11,2) COMMENT '二级分佣金额'
)
comment '订单结算表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 退款订单表
DROP TABLE if exists yp_ods.t_refund_order;
CREATE TABLE yp_ods.t_refund_order
(
id string COMMENT '退款单号',
order_id string COMMENT '订单的id',
apply_date string COMMENT '用户申请退款的时间',
modify_date string COMMENT '退款订单更新时间',
refund_reason string COMMENT '买家退款原因',
refund_amount DECIMAL(11,2) COMMENT '订单退款的金额',
refund_state TINYINT COMMENT '1.申请退款;2.拒绝退款; 3.同意退款,配送员配送; 4:商家同意退款,用户亲自送货 ;5.退款完成',
refuse_refund_reason string COMMENT '商家拒绝退款原因',
refund_goods_type string COMMENT '1.上门取货(买家承担运费); 2.买家送达;',
refund_shipping_fee DECIMAL(11,2) COMMENT '配送员的运费(如果退货方式为1:则买家支付配送费)',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '退款订单表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 订单分组表
DROP TABLE if exists yp_ods.t_shop_order_group;
CREATE TABLE yp_ods.t_shop_order_group
(
id string,
order_id string COMMENT '订单id',
group_id string COMMENT '订单分组id',
is_pay TINYINT COMMENT '是否已支付,0未支付,1已支付',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT
)
comment '订单分组表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 店铺表
DROP TABLE if exists yp_ods.t_store;
CREATE TABLE yp_ods.t_store (
`id` string COMMENT '主键',
`user_id` string,
`store_avatar` string COMMENT '店铺头像',
`address_info` string COMMENT '店铺详细地址',
`name` string COMMENT '店铺名称',
`store_phone` string COMMENT '联系电话',
`province_id` INT COMMENT '店铺所在省份ID',
`city_id` INT COMMENT '店铺所在城市ID',
`area_id` INT COMMENT '店铺所在县ID',
`mb_title_img` string COMMENT '手机店铺 页头背景图',
`store_description` string COMMENT '店铺描述',
`notice` string COMMENT '店铺公告',
`is_pay_bond` TINYINT COMMENT '是否有交过保证金 1:是0:否',
`trade_area_id` string COMMENT '归属商圈ID',
`delivery_method` TINYINT COMMENT '配送方式 1 :自提 ;3 :自提加配送均可; 2 : 商家配送',
`origin_price` DECIMAL,
`free_price` DECIMAL,
`store_type` INT COMMENT '店铺类型 22天街网店 23实体店 24直营店铺 33会员专区店',
`store_label` string COMMENT '店铺logo',
`search_key` string COMMENT '店铺搜索关键字',
`end_time` string COMMENT '营业结束时间',
`start_time` string COMMENT '营业开始时间',
`operating_status` TINYINT COMMENT '营业状态 0 :未营业 ;1 :正在营业',
`create_user` string,
`create_time` string,
`update_user` string,
`update_time` string,
`is_valid` TINYINT COMMENT '0关闭,1开启,3店铺申请中',
`state` string COMMENT '可使用的支付类型:MONEY金钱支付;CASHCOUPON现金券支付',
`idCard` string COMMENT '身份证',
`deposit_amount` DECIMAL(11,2) COMMENT '商圈认购费用总额',
`delivery_config_id` string COMMENT '配送配置表关联ID',
`aip_user_id` string COMMENT '通联支付标识ID',
`search_name` string COMMENT '模糊搜索名称字段:名称_+真实名称',
`automatic_order` TINYINT COMMENT '是否开启自动接单功能 1:是 0 :否',
`is_primary` TINYINT COMMENT '是否是总店 1: 是 2: 不是',
`parent_store_id` string COMMENT '父级店铺的id,只有当is_primary类型为2时有效'
)
comment '店铺表'
partitioned by (dt string)
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 订单和商品的中间表
DROP TABLE if exists yp_ods.t_shop_order_goods_details;
CREATE TABLE yp_ods.t_shop_order_goods_details
(
id string COMMENT 'id主键',
order_id string COMMENT '对应订单表的id',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
goods_id string COMMENT '购买商品的id',
buy_num INT COMMENT '购买商品的数量',
goods_price DECIMAL(11,2) COMMENT '购买商品的价格',
total_price DECIMAL(11,2) COMMENT '购买商品的价格 = 商品的数量 * 商品的单价 ',
goods_name string COMMENT '商品的名称',
goods_image string COMMENT '商品的图片',
goods_specification string COMMENT '商品规格',
goods_weight INT,
goods_unit string COMMENT '商品计量单位',
goods_type string COMMENT '商品分类 ytgj:进口商品 ytsc:普通商品 hots爆品',
refund_order_id string COMMENT '退款订单的id',
goods_brokerage DECIMAL(11,2) COMMENT '商家设置的商品分润的金额',
is_refund TINYINT COMMENT '0.不退款; 1.退款',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true'
)
comment '订单和商品的中间表'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 购物车
DROP TABLE if exists yp_ods.t_shop_cart;
CREATE TABLE yp_ods.t_shop_cart
(
id string COMMENT '主键id',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
goods_id string COMMENT '购买商品的id',
buy_num INT COMMENT '购买商品的数量',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true'
)
comment '购物车'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 店铺收藏
DROP TABLE if exists yp_ods.t_store_collect;
CREATE TABLE yp_ods.t_store_collect
(
id string,
user_id string COMMENT '收藏人id',
store_id string COMMENT '店铺id',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '店铺收藏'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
-- 商品收藏
DROP TABLE if exists yp_ods.t_goods_collect;
CREATE TABLE yp_ods.t_goods_collect
(
id string,
user_id string COMMENT '收藏人id',
goods_id string COMMENT '商品id',
store_id string COMMENT '通过哪个店铺收藏的(因主店分店概念存在需要)',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启'
)
comment '商品收藏'
partitioned by (dt string) row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress'='ZLIB');
6.9通过sqoop将数据导入到ods层
1- 采用全量覆盖导入操作(第一次):
# 日期表: t_date
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query 'select * from t_date where 1 = 1 and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_date' \
--fields-terminated-by '\t' \
-m 1
2- 仅新增同步方式的表 导入操作: 第一次导入(范围是什么? 截止到上一天的所有的数据)
# 订单评价表 : t_goods_evaluation
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query 'select *, "2022-04-26" as dt from t_goods_evaluation where create_time <= "2022-04-26 23:59:59" and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_goods_evaluation' \
--fields-terminated-by '\t' \
-m 1
3- 新增及更新同步方式的表导入操作: 第一次导入
# 订单表
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query 'select *, "2022-04-26" as dt from t_shop_order where create_time <= "2022-04-26 23:59:59" OR update_time <= "2022-04-26 23:59:59" and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_shop_order' \
--fields-terminated-by '\t' \
-m 1
如果要获取上一天的新增和更新的数据:
select *, "2022-04-26" as dt from t_shop_order where create_time between "2022-04-26 00:00:00" and "2022-04-26 23:59:59" OR update_time between "2022-04-26 00:00:00" and "2022-04-26 23:59:59"
7.ods层增量数据采集操作
7.1.模拟一份增量数据操作:
-- 日期表(全量覆盖的方式):
-- 新增一条数据
INSERT INTO yipin.`t_date` VALUES ('20310101','2031-01-01','20301201','2031','2031年','01','01月','1','Q1','203101','52','203052','01','203101','3','星期三','01','01','4','3','01','001','20301225','20301201','10000000','20300101','20310101','20310131','1','上半年','S04','冬季','否','H01','元旦','F01','元旦','','','2021-12-20 14:20:57.401');
-- 订单评价表(仅新增同步):
INSERT INTO yipin.`t_goods_evaluation` VALUES ('10001','430eff5a55d911e998ec7cd30ad32e2e','7b09b44e5b6d11e998ec7cd30ad32e2e','dd190411306814f41f',10,10,10,1,'430eff5a55d911e998ec7cd30ad32e2e','2022-04-27 09:42:02',NULL,NULL,1);
-- 店铺表(新增及更新同步):
update yipin.`t_store` set name='北京传智教育科技股份有限公司' , update_time = '2022-04-27 05:50:55' where id = '0afb5daf777d11e998ec7cd30ad32e2e';
INSERT INTO yipin.`t_store` VALUES ('10010','02554155777c11e998ec7cd30ad32e2e',NULL,NULL,'黑马程序员',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'0af148be777d11e998ec7cd30ad32e2e',NULL,NULL,NULL,24,NULL,NULL,'22:00','08:00',0,'02554155777c11e998ec7cd30ad32e2e','2022-04-27 05:50:55',NULL,NULL,3,'MONEY',NULL,NULL,'0afaf8ad777d11e998ec7cd30ad32e2e',NULL,'名称_**亿隆电子科技开发有限公司',0,1,NULL);
7.2.完成增量数据采集操作
全量覆盖的表操作:日期表
– 日期表(全量覆盖): 如果更新的频次以天基准, 对于全量覆盖的表来说, 每天都是将之前的所有的数据全部都删除, 然后重新导入即可
– 思考: 是否可以通过sqoop直接进行全量覆盖呢? 表的存储格式为ORC , 导入数据需要使用hcatalog方式, hcatalog是否支持覆盖导入呢? 不行的, 仅支持追加
-- 如何解决呢? 先将表清空了, 然后执行导入操作
-- 对于 hive的表, 是无法对数据直接进行处理, 也就是无法修改数据, 或者删除数据, 一般建议将表先删除, 然后重新建表即可
drop table if exists yp_ods.t_date;
CREATE TABLE if not exists yp_ods.t_date (
dim_date_id string COMMENT '日期',
date_code string COMMENT '日期编码',
lunar_calendar string COMMENT '农历',
year_code string COMMENT '年code',
year_name string COMMENT '年名称',
month_code string COMMENT '月份编码',
month_name string COMMENT '月份名称',
quanter_code string COMMENT '季度编码',
quanter_name string COMMENT '季度名称',
year_month string COMMENT '年月',
year_week_code string COMMENT '一年中第几周',
year_week_name string COMMENT '一年中第几周名称',
year_week_code_cn string COMMENT '一年中第几周(中国)',
year_week_name_cn string COMMENT '一年中第几周名称(中国',
week_day_code string COMMENT '周几code',
week_day_name string COMMENT '周几名称',
day_week string COMMENT '周',
day_week_cn string COMMENT '周(中国)',
day_week_num string COMMENT '一周第几天',
day_week_num_cn string COMMENT '一周第几天(中国)',
day_month_num string COMMENT '一月第几天',
day_year_num string COMMENT '一年第几天',
date_id_wow string COMMENT '与本周环比的上周日期',
date_id_mom string COMMENT '与本月环比的上月日期',
date_id_wyw string COMMENT '与本周同比的上年日期',
date_id_mym string COMMENT '与本月同比的上年日期',
first_date_id_month string COMMENT '本月第一天日期',
last_date_id_month string COMMENT '本月最后一天日期',
half_year_code string COMMENT '半年code',
half_year_name string COMMENT '半年名称',
season_code string COMMENT '季节编码',
season_name string COMMENT '季节名称',
is_weekend string COMMENT '是否周末(周六和周日)',
official_holiday_code string COMMENT '法定节假日编码',
official_holiday_name string COMMENT '法定节假日',
festival_code string COMMENT '节日编码',
festival_name string COMMENT '节日',
custom_festival_code string COMMENT '自定义节日编码',
custom_festival_name string COMMENT '自定义节日',
update_time string COMMENT '更新时间'
)
COMMENT '时间维度表'
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
-- 基于 sqoop 完成 全量的数据采集操作:
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query 'select * from t_date where 1 = 1 and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_date' \
--fields-terminated-by '\t' \
-m 1
7.3.仅新增同步方式:
-- 订单评价表(仅新增同步): 表只有新增的操作, 没有更新的操作, 对于这种同步方式的表, 我们只需要每天将其新增的数据导入到对应的分区即可
-- 如何获取上一天的新增数据呢?
select *, '2022-04-27' as dt from yipin.`t_goods_evaluation` where create_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59';
-- 基于 sqoop完成导入:
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query "select *, '2022-04-27' as dt from t_goods_evaluation where create_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59' and \$CONDITIONS" \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_goods_evaluation' \
--fields-terminated-by '\t' \
-m 1
7.4.新增和更新同步的方式:
-- 店铺表(新增及更新同步): 每天进行增量数据同步的时候, 需要将上一天的新增的数据和更新的数据放置到对应的分区即可
-- 思考: 如果获取到上一天的新增和更新的数据呢?
select *, '2022-04-27' as dt from yipin.t_store where create_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59' OR update_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59';
-- 基于 sqoop完成数据导入操作
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query "select *, '2022-04-27' as dt from t_store where create_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59' OR update_time BETWEEN '2022-04-27 00:00:00' and '2022-04-27 23:59:59' and \$CONDITIONS" \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_store' \
--fields-terminated-by '\t' \
-m 1
7.5.编写shell脚本,完成增量脚本实现:
1- 在hadoop01的家目录下执行操作:
cd ~
vim yp_ods_incr.sh
输入 i 进入编辑模式:
添加以下内容:
#!/bin/bash
# 1- env path
HIVE_HOME=/usr/bin/hive
# 2- Tran
if [ $# == 1 ]
then
dateStr=$1
else
dateStr=`date -d '-1 day' +'%Y-%m-%d'`
fi
echo ${dateStr}
# 3- HIVE delete table
echo '---------------HIVE DELETE START---------------'
${HIVE_HOME} -S -e "
drop table if exists yp_ods.t_date;
CREATE TABLE if not exists yp_ods.t_date (
dim_date_id string COMMENT '日期',
date_code string COMMENT '日期编码',
lunar_calendar string COMMENT '农历',
year_code string COMMENT '年code',
year_name string COMMENT '年名称',
month_code string COMMENT '月份编码',
month_name string COMMENT '月份名称',
quanter_code string COMMENT '季度编码',
quanter_name string COMMENT '季度名称',
year_month string COMMENT '年月',
year_week_code string COMMENT '一年中第几周',
year_week_name string COMMENT '一年中第几周名称',
year_week_code_cn string COMMENT '一年中第几周(中国)',
year_week_name_cn string COMMENT '一年中第几周名称(中国',
week_day_code string COMMENT '周几code',
week_day_name string COMMENT '周几名称',
day_week string COMMENT '周',
day_week_cn string COMMENT '周(中国)',
day_week_num string COMMENT '一周第几天',
day_week_num_cn string COMMENT '一周第几天(中国)',
day_month_num string COMMENT '一月第几天',
day_year_num string COMMENT '一年第几天',
date_id_wow string COMMENT '与本周环比的上周日期',
date_id_mom string COMMENT '与本月环比的上月日期',
date_id_wyw string COMMENT '与本周同比的上年日期',
date_id_mym string COMMENT '与本月同比的上年日期',
first_date_id_month string COMMENT '本月第一天日期',
last_date_id_month string COMMENT '本月最后一天日期',
half_year_code string COMMENT '半年code',
half_year_name string COMMENT '半年名称',
season_code string COMMENT '季节编码',
season_name string COMMENT '季节名称',
is_weekend string COMMENT '是否周末(周六和周日)',
official_holiday_code string COMMENT '法定节假日编码',
official_holiday_name string COMMENT '法定节假日',
festival_code string COMMENT '节日编码',
festival_name string COMMENT '节日',
custom_festival_code string COMMENT '自定义节日编码',
custom_festival_name string COMMENT '自定义节日',
update_time string COMMENT '更新时间'
)
COMMENT '时间维度表'
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'ZLIB');
"
echo '---------------HIVE DELETE SUCCESS---------------'
# 4- SQOOP IMPORT
echo '---------------SQOOP IMPORT START---------------'
SQOOP_HOME=/usr/bin/sqoop
# PUBLIC PATH
url='jdbc:mysql://hadoop01:3306/yipin'
username='root'
password='123456'
${SQOOP_HOME} import \
--connect ${url} \
--username ${username} \
--password ${password} \
--query 'select * from t_date where 1 = 1 and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_date' \
--fields-terminated-by '\t' \
-m 1
wait
${SQOOP_HOME} import \
--connect ${url} \
--username ${username} \
--password ${password} \
--query "select *, '${dateStr}' as dt from t_goods_evaluation where create_time BETWEEN '${dateStr} 00:00:00' and '${dateStr} 23:59:59' and \$CONDITIONS" \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_goods_evaluation' \
--fields-terminated-by '\t' \
-m 1
wait
${SQOOP_HOME} import \
--connect ${url} \
--username ${username} \
--password ${password} \
--query "select *, '${dateStr}' as dt from t_store where create_time BETWEEN '${dateStr} 00:00:00' and '${dateStr} 23:59:59' OR update_time BETWEEN '${dateStr} 00:00:00' and '${dateStr} 23:59:59' and \$CONDITIONS" \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_store' \
--fields-terminated-by '\t' \
-m 1
echo '---------------SQOOP IMPORT SUCCESS---------------'
执行shell脚本,执行完成后,进行校验操作
全量覆盖表,主要是校验所有的数据是否和数据源表一致
8.分通表的相关内容
8.1.分桶表基本介绍
分文件的, 在创建表的时候, 指定分桶字段, 并设置分多少个桶, 在添加数据的时候, hive会根据设置分桶字段, 将数据划分到N个桶(文件)中, 默认情况采用HASH分桶方案 , 分多少个桶, 取决于建表的时候, 设置分桶数量, 分了多少个桶最终翻译的MR也就会运行多少个reduce程序(HIVE的分桶本质上就是MR的分区操作)
create table 表名(
字段 类型,
....
)
clustered by(分桶字段) [sorted by (字段 [asc | desc])] into N buckets --- 定义分桶表核心语句
row format......
8.2.如何向桶表中添加数据
思考: 是否可以通过 load data 方式添加数据呢? 不行的
注意: 如果使用 apache 版本的HIVE, 默认情况下, 是可以通过 load data 方式来加载数据. 只不过没有分桶的效果
但是对于 CDH版本中, 是不允许通过 load data 方式来加载的:
在CDH中默认开启了一个参数, 禁止采用load data方式向桶表添加数据: set hive.strict.checks.bucketing = true;
如果 现有一个文本文件数据, 需要加载到分桶表,如何解决呢?
第一步: 基于桶表创建一张临时表, 此表和桶表保持相同字段, 唯一区别, 当前这个表不是一个桶表
第二步: 将数据先加载到这个临时表中
第三步: 基于临时表, 使用 insert into|overwrite + select 将数据添加到桶表
注意: sqoop不支持桶表数据导入操作
8.3.桶表有什么作用?
1) 进行数据采样工作
1.1) 当表的数据量比较庞大的时候, 在编写SQL语句后, 需要首先测试 SQL是否可以正常的执行, 需要在表中执行查询操作, 由于表数据量比较庞大, 在测试一条SQL的时候整个运行的时间比较久, 为了提升测试效率, 可以整个表抽样出一部分的数据, 进行测试
1.2) 校验数据的可行性(质量校验)
1.3) 进行统计分析的时候, 并不需要统计出具体的指标, 可能统计的都是一些相对性指标, 比如说一些比率(合格率)问题, 此时可以通过采样处理
2) 提升查询的效率(更主要是提升JOIN的效率)
可以减少JOIN次数, 从而提升效率
注意:
在生产环境中, 何时使用桶表, 主要看是否需要应用上述作用
8.4.数据采样
如何实现数据采样:
采样函数:
tablesample(bucket x out of y [on column]
使用位置: 紧紧跟在表名的后面, 如果表名有别名, 必须放置别名的前面
说明:
x: 从第几个桶开始进行采样
y: 抽样比例
column: 分桶的字段, 可以省略
注意:
x 不能大于 y
y 必须是表的分桶数量的倍数或者因子
案例:
1) 假设 A表有10个桶, 请分析, 下面的采样函数, 会将那些桶抽取出来呢?
tablesample(bucket 2 out of 5 on xxx)
会抽取出几个桶数据呢? 总桶数 / 抽样比例 = 分桶数量 2个桶
抽取那几个桶呢? (x + y)
2, 7
2) 假设 A 表有20个桶, 请分析, 下面的抽样函数, 会将那些桶抽取出来呢?
tablesample(bucket 4 out of 4 on xxx)
会抽取出几个桶数据呢? 总桶数 / 抽样比例 = 分桶数量 5个桶
抽取那几个桶呢?
4 , 8,12,16,20
tablesample(bucket 8 out of 40 on xxx)
会抽取出几个桶数据呢? 总桶数 / 抽样比例 = 分桶数量 二分之一个桶
抽取那几个桶呢?
8号桶二分之一
大多数情况下, 都是因子, 取某几个桶的操作
8.5.执行计划
用户提交HiveSQL查询后,Hive会吧查询语句转换为MapReduce作业.Hive会自动完成整个执行计划过程,一般情况下,我们并不用知道内部是如何运行的.
执行计划可以告诉我们查询过程的关键信息,用来帮助我们判定优化措施是否已经生效
EXPLAIN [EXTENDED] query
简单来说:
explain sql 语句
1. stage依赖(STAGE DEPENDENCIES)
(1) 这部分展示本次查询分为两个stage:Stage-1,Stage-0.
(2) 一般Stage-0是最终给查询用户展示数据用的,如LIMITE操作就会在这部分。
(3) Stage-1是mr程序的执行阶段。
1 STAGE DEPENDENCIES:
2 Stage-1 is a root stage
3 Stage-0 depends on stages: Stage-1
2. stage详细执行计划(STAGE PLANS)
(1) 包含了整个查询所有Stage的大部分处理过程。
(2) 特定优化是否生效,主要通过此部分内容查看。
3. 名次解释
TableScan:查看表
alias: emp:所需要的表
Statistics: Num rows: 2 Data size: 820 Basic stats: COMPLETE Column stats: NONE:这张表 的基本统计信息:行数、大小等;
expressions: empno (type: int), ename (type: string), job (type: string), mgr (type: int), hiredate (type: string), sal (type: double), comm (type: double), deptno (type: int):表中需要输出 的字段及类型
outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7:输出的的字段编 号
compressed: true:输出是否压缩;
input format: org.apache.hadoop.mapred.SequenceFileInputFormat:文件输入调用的Java 类,显示以文本Text格式输入;
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat:文件输出调 用的java类,显示以文本Text格式输出;
8.6.将ods层某个表进行分桶重建演示
以订单为例,进行ods层的分桶重建操作
8.6.1ods层对订单进行重建操作
-- 订单表
DROP TABLE if exists yp_ods.t_shop_order;
CREATE TABLE yp_ods.t_shop_order
(
id string COMMENT '根据一定规则生成的订单编号',
order_num string COMMENT '订单序号',
buyer_id string COMMENT '买家的userId',
store_id string COMMENT '店铺的id',
order_from TINYINT COMMENT '是来自于app还是小程序,或者pc 1.安卓; 2.ios; 3.小程序H5 ; 4.PC',
order_state INT COMMENT '订单状态:1.已下单; 2.已付款, 3. 已确认 ;4.配送; 5.已完成; 6.退款;7.已取消',
create_date string COMMENT '下单时间',
finnshed_time timestamp COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
is_settlement TINYINT COMMENT '是否结算;0.待结算订单; 1.已结算订单;',
is_delete TINYINT COMMENT '订单评价的状态:0.未删除; 1.已删除;(默认0)',
evaluation_state TINYINT COMMENT '订单评价的状态:0.未评价; 1.已评价;(默认0)',
way string COMMENT '取货方式:SELF自提;SHOP店铺负责配送',
is_stock_up INT COMMENT '是否需要备货 0:不需要 1:需要 2:平台确认备货 3:已完成备货 4平台已经将货物送至店铺 ',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '订单表'
partitioned by (dt string)
clustered by(id) sorted by (id) into 5 buckets
row format delimited fields terminated by '\t'
stored as orc tblproperties ('orc.compress' = 'ZLIB');
8.6.2将数据导入到ods层
创建一张订单表的临时表(一定不能是桶表)
CREATE TABLE yp_ods.t_shop_order_temp
(
id string COMMENT '根据一定规则生成的订单编号',
order_num string COMMENT '订单序号',
buyer_id string COMMENT '买家的userId',
store_id string COMMENT '店铺的id',
order_from TINYINT COMMENT '是来自于app还是小程序,或者pc 1.安卓; 2.ios; 3.小程序H5 ; 4.PC',
order_state INT COMMENT '订单状态:1.已下单; 2.已付款, 3. 已确认 ;4.配送; 5.已完成; 6.退款;7.已取消',
create_date string COMMENT '下单时间',
finnshed_time timestamp COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
is_settlement TINYINT COMMENT '是否结算;0.待结算订单; 1.已结算订单;',
is_delete TINYINT COMMENT '订单评价的状态:0.未删除; 1.已删除;(默认0)',
evaluation_state TINYINT COMMENT '订单评价的状态:0.未评价; 1.已评价;(默认0)',
way string COMMENT '取货方式:SELF自提;SHOP店铺负责配送',
is_stock_up INT COMMENT '是否需要备货 0:不需要 1:需要 2:平台确认备货 3:已完成备货 4平台已经将货物送至店铺 ',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志'
)
comment '订单表'
partitioned by (dt string)
row format delimited fields terminated by '\t'
stored as orc tblproperties ('orc.compress' = 'ZLIB');
8.6.3通过sqoop将数据导入到临时表中
# 订单表
sqoop import \
--connect jdbc:mysql://hadoop01:3306/yipin \
--username root \
--password 123456 \
--query 'select *, "2022-04-26" as dt from t_shop_order where create_time <= "2022-04-26 23:59:59" OR update_time <= "2022-04-26 23:59:59" and $CONDITIONS' \
--hcatalog-database 'yp_ods' \
--hcatalog-table 't_shop_order_temp' \
--fields-terminated-by '\t' \
-m 1
8.6.4从临时表将数据灌入到桶表中
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true; -- 开启动态分区支持, 默认为true
SET hive.exec.dynamic.partition.mode=nonstrict; -- 开启非严格模式
set hive.exec.max.dynamic.partitions.pernode=10000; -- 最大一个节点能够生成多少个分区
set hive.exec.max.dynamic.partitions=100000; -- 最大支持多少个动态分区
set hive.exec.max.created.files=150000; -- 最多一次性可以创建多少个文件
-- hive压缩
set hive.exec.compress.intermediate=true; -- 中间结果的压缩 默认为true
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
--分桶
set hive.enforce.bucketing=true; -- 开启桶表支持 (默认为true)
set hive.enforce.sorting=true; -- 开启分桶强制排序, 根据sort by 指定字段排序
insert overwrite table yp_ods.t_shop_order partition(dt)
select
*
from yp_ods.t_shop_order_temp;
注意: set 设置 仅在当前会话有效
演示采用操作,比如说,要采样第三个桶
explain
select
*
from yp_ods.t_shop_order tablesample(bucket 3 out of 5);
执行计划
# 描述 共计有二个阶段: stage -1 和 stage -0 其中 -0阶段依赖于 -1阶段, 先执行 -1阶段
1 STAGE DEPENDENCIES:
2 Stage-1 is a root stage
3 Stage-0 depends on stages: Stage-1
4
# 各个节点详细描述
5 STAGE PLANS:
6 Stage: Stage-1
7 Map Reduce
8 Map Operator Tree:
9 TableScan
# 读取 t_shop_order这个表
10 alias: t_shop_order
# 数量信息: 共有 3155条消息 总数据大小 4044436
11 Statistics: Num rows: 3155 Data size: 4044436 Basic stats: COMPLETE Column stats: PARTIAL
# 描述过滤信息
12 Filter Operator
# (((hash(id) & 2147483647) % 5) = 2) : 对 id 进行 hash取模 对 5取模 余数为 2 表示第三个桶
13 predicate: (((hash(id) & 2147483647) % 5) = 2) (type: boolean)
14 Statistics: Num rows: 1577 Data size: 290168 Basic stats: COMPLETE Column stats: PARTIAL
15 Select Operator
16 expressions: id (type: string), order_num (type: string), buyer_id (type: string), store_id (type: string), order_from (type: tinyint), order_state (type: int), create_date (type: string), finnshed_time (type: timestamp), is_settlement (type: tinyint), is_delete (type: tinyint), evaluation_state (type: tinyint), way (type: string), is_stock_up (type: int), create_user (type: string), create_time (type: string), update_user (type: string), update_time (type: string), is_valid (type: tinyint), dt (type: string)
17 outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7, _col8, _col9, _col10, _col11, _col12, _col13, _col14, _col15, _col16, _col17, _col18
18 Statistics: Num rows: 1577 Data size: 290168 Basic stats: COMPLETE Column stats: PARTIAL
19 File Output Operator
20 compressed: true
21 Statistics: Num rows: 1577 Data size: 290168 Basic stats: COMPLETE Column stats: PARTIAL
22 table:
23 input format: org.apache.hadoop.mapred.SequenceFileInputFormat
24 output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
25 serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
26
27 Stage: Stage-0
28 Fetch Operator
29 limit: -1
30 Processor Tree:
31 ListSink
9.dwd层相关操作
dwd层作用:
dwd层和ods层保持相同粒度,从ods层将数据抽取出来,对数据进行清洗转换的操作,将清洗转换后的数据灌入到dwd层中
从dwd层开始,将整体迁移到云环境来处理
云环境的HUE地址:
http://106.75.33.59:8888/hue/
用户名: bj_class59
密码: bj_class59
9.1.构建dwd层的库
create database if not exists bj59_yp_dwd_jiale ;
库名格式要求(务必按照此格式构建):
bj59_yp_dwd_姓名
9.2.说明:关于在dwd层,一般主要做那些清洗和转换的操作
关于当前项目的DWD层构建:
1) 由于粒度是一致的, 所以DWD层表数量以及表的结构基本上ODS层是一致的
2) 在DWD层建表的时候, 将压缩方案 从原有zlib 更改为 snappy, 便于后续读取操作
3) 对于同步方式为新增及更新的表, 由于需要在DWD层中对历史数据进行拉链处理操作, 所以在DWD层进行建表的时候, 会新建两个字段: start_date(拉链开始时间) 和 end_date(拉链的结束时间) 其中 会将 start_date作为分区字段
一般在实际生产环境中 一般需要清洗转换那些操作呢?
1- 去除无用空值, 缺少值
2- 去重
3- 过滤掉一些以及标记为删除的数据
4- 发现一个字段中如果涵盖了多个字段信息, 一般需要将其转换为 多个字段来分别处理
比如说: 日期, ODS层中日期值可能为 2022-01-01 14:25:30 包含 年 月 日 小时 分钟 秒
可以将其拆解为 年字段, 月字段, 日字段, 小时字段, 季度字段...
5- 原有数据可能是通过数字来表示一种行为, 可能需要将其转换为具体的内容
比如说: 数据中用 1表示男性 0 表示女性 直接将其转换为 男 和 女
或者 将具体内容, 转换为数字
6- 维度退化操作, 将多个相关的表合并为一个表(此种一般需要JOIN大量的表来处理)
注意: 一般此操作会独立处理, 不会和清洗转换放置在一块, 除非非常简单
7- JSOIN数据的拉平操作:
比如说: 一个字段为 content字段, 字段里面数据格式 "{'name':'张三','address':'北京'}"
此时需要将其拉宽拉平, 形成两个新的字段: content_name , content_address
目前, 在我们项目中, 基本上不去处理任何的转换操作, 主要原因是因为当前这份数据, 本身就是一些测试数据, 里面将大量的敏感数据给脱敏了, 导致一旦进行清洗处理, 可能什么数据都不剩下了
9.3.事实表建表语句:
--订单事实表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_shop_order;
CREATE TABLE bj59_yp_dwd_jiale.fact_shop_order(
id string COMMENT '根据一定规则生成的订单编号',
order_num string COMMENT '订单序号',
buyer_id string COMMENT '买家的userId',
store_id string COMMENT '店铺的id',
order_from string COMMENT '此字段可以转换 1.安卓\; 2.ios\; 3.小程序H5 \; 4.PC',
order_state int COMMENT '订单状态:1.已下单\; 2.已付款, 3. 已确认 \;4.配送\; 5.已完成\; 6.退款\;7.已取消',
create_date string COMMENT '下单时间',
finnshed_time timestamp COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
is_settlement tinyint COMMENT '是否结算\;0.待结算订单\; 1.已结算订单\;',
is_delete tinyint COMMENT '订单评价的状态:0.未删除\; 1.已删除\;(默认0)',
evaluation_state tinyint COMMENT '订单评价的状态:0.未评价\; 1.已评价\;(默认0)',
way string COMMENT '取货方式:SELF自提\;SHOP店铺负责配送',
is_stock_up int COMMENT '是否需要备货 0:不需要 1:需要 2:平台确认备货 3:已完成备货 4平台已经将货物送至店铺 ',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true\; 订单是否有效的标志',
end_date string COMMENT '拉链结束日期')
COMMENT '订单表'
partitioned by (start_date string)
CLUSTERED BY(id) SORTED BY(id) INTO 5 BUCKETS
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单详情表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_shop_order_address_detail;
CREATE TABLE bj59_yp_dwd_jiale.fact_shop_order_address_detail(
id string COMMENT '关联订单的id',
order_amount decimal(36,2) COMMENT '订单总金额:购买总金额-优惠金额',
discount_amount decimal(36,2) COMMENT '优惠金额',
goods_amount decimal(36,2) COMMENT '用户购买的商品的总金额+运费',
is_delivery string COMMENT '0.自提;1.配送',
buyer_notes string COMMENT '买家备注留言',
pay_time string,
receive_time string,
delivery_begin_time string,
arrive_store_time string,
arrive_time string COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true\; 订单是否有效的标志',
end_date string COMMENT '拉链结束日期')
COMMENT '订单详情表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单结算表(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_order_settle;
CREATE TABLE bj59_yp_dwd_jiale.fact_order_settle(
id string COMMENT '结算单号',
order_id string,
settlement_create_date string COMMENT '用户申请结算的时间',
settlement_amount decimal(36,2) COMMENT '如果发生退款,则结算的金额 = 订单的总金额 - 退款的金额',
dispatcher_user_id string COMMENT '配送员id',
dispatcher_money decimal(36,2) COMMENT '配送员的配送费(配送员的运费(如果退货方式为1:则买家支付配送费))',
circle_master_user_id string COMMENT '圈主id',
circle_master_money decimal(36,2) COMMENT '圈主分润的金额',
plat_fee decimal(36,2) COMMENT '平台应得的分润',
store_money decimal(36,2) COMMENT '商家应得的订单金额',
status tinyint COMMENT '0.待结算;1.待审核 \; 2.完成结算;3.拒绝结算',
note string COMMENT '原因',
settle_time string COMMENT ' 结算时间',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true\; 订单是否有效的标志',
first_commission_user_id string COMMENT '一级分佣用户',
first_commission_money decimal(36,2) COMMENT '一级分佣金额',
second_commission_user_id string COMMENT '二级分佣用户',
second_commission_money decimal(36,2) COMMENT '二级分佣金额',
end_date string COMMENT '拉链结束日期')
COMMENT '订单结算表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--退款订单表(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_refund_order;
CREATE TABLE bj59_yp_dwd_jiale.fact_refund_order
(
id string COMMENT '退款单号',
order_id string COMMENT '订单的id',
apply_date string COMMENT '用户申请退款的时间',
modify_date string COMMENT '退款订单更新时间',
refund_reason string COMMENT '买家退款原因',
refund_amount DECIMAL(11,2) COMMENT '订单退款的金额',
refund_state TINYINT COMMENT '1.申请退款;2.拒绝退款; 3.同意退款,配送员配送; 4:商家同意退款,用户亲自送货 ;5.退款完成',
refuse_refund_reason string COMMENT '商家拒绝退款原因',
refund_goods_type string COMMENT '1.上门取货(买家承担运费); 2.买家送达;',
refund_shipping_fee DECIMAL(11,2) COMMENT '配送员的运费(如果退货方式为1:则买家支付配送费)',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志',
end_date string COMMENT '拉链结束日期'
)
comment '退款订单表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单组表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_shop_order_group;
CREATE TABLE bj59_yp_dwd_jiale.fact_shop_order_group(
id string,
order_id string COMMENT '订单id',
group_id string COMMENT '订单分组id',
is_pay tinyint COMMENT '是否已支付,0未支付,1已支付',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint,
end_date string COMMENT '拉链结束日期')
COMMENT '订单组'
partitioned by (start_date string)
CLUSTERED BY(order_id ) SORTED BY(order_id ) INTO 5 BUCKETS
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单组支付(增量表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_order_pay;
CREATE TABLE bj59_yp_dwd_jiale.fact_order_pay(
id string,
group_id string COMMENT '关联shop_order_group的group_id,一对多订单',
order_pay_amount decimal(36,2) COMMENT '订单总金额\;',
create_date string COMMENT '订单创建的时间,需要根据订单创建时间进行判断订单是否已经失效',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true\; 订单是否有效的标志'
)
COMMENT '订单组支付表'
partitioned by (dt string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单商品快照(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_shop_order_goods_details;
CREATE TABLE bj59_yp_dwd_jiale.fact_shop_order_goods_details(
id string COMMENT 'id主键',
order_id string COMMENT '对应订单表的id',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
goods_id string COMMENT '购买商品的id',
buy_num int COMMENT '购买商品的数量',
goods_price decimal(36,2) COMMENT '购买商品的价格',
total_price decimal(36,2) COMMENT '购买商品的价格 = 商品的数量 * 商品的单价 ',
goods_name string COMMENT '商品的名称',
goods_image string COMMENT '商品的图片',
goods_specification string COMMENT '商品规格',
goods_weight int,
goods_unit string COMMENT '商品计量单位',
goods_type string COMMENT '商品分类 ytgj:进口商品 ytsc:普通商品 hots爆品',
refund_order_id string COMMENT '退款订单的id',
goods_brokerage decimal(36,2) COMMENT '商家设置的商品分润的金额',
is_refund tinyint COMMENT '0.不退款\; 1.退款',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true',
end_date string COMMENT '拉链结束日期')
COMMENT '订单商品快照'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--订单评价表(增量表,与ODS一致)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_goods_evaluation;
CREATE TABLE bj59_yp_dwd_jiale.fact_goods_evaluation(
id string,
user_id string COMMENT '评论人id',
store_id string COMMENT '店铺id',
order_id string COMMENT '订单id',
geval_scores int COMMENT '综合评分',
geval_scores_speed int COMMENT '送货速度评分0-5分(配送评分)',
geval_scores_service int COMMENT '服务评分0-5分',
geval_isanony tinyint COMMENT '0-匿名评价,1-非匿名',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0 :失效,1 :开启')
COMMENT '订单评价表'
partitioned by (dt string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--商品评价表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_goods_evaluation_detail;
CREATE TABLE bj59_yp_dwd_jiale.fact_goods_evaluation_detail(
id string,
user_id string COMMENT '评论人id',
store_id string COMMENT '店铺id',
goods_id string COMMENT '商品id',
order_id string COMMENT '订单id',
order_goods_id string COMMENT '订单商品表id',
geval_scores_goods int COMMENT '商品评分0-10分',
geval_content string,
geval_content_superaddition string COMMENT '追加评论',
geval_addtime string COMMENT '评论时间',
geval_addtime_superaddition string COMMENT '追加评论时间',
geval_state tinyint COMMENT '评价状态 1-正常 0-禁止显示',
geval_remark string COMMENT '管理员对评价的处理备注',
revert_state tinyint COMMENT '回复状态0未回复1已回复',
geval_explain string COMMENT '管理员回复内容',
geval_explain_superaddition string COMMENT '管理员追加回复内容',
geval_explaintime string COMMENT '管理员回复时间',
geval_explaintime_superaddition string COMMENT '管理员追加回复时间',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
COMMENT '商品评价明细'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--配送表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.fact_order_delievery_item;
CREATE TABLE bj59_yp_dwd_jiale.fact_order_delievery_item(
id string COMMENT '主键id',
shop_order_id string COMMENT '订单表ID',
refund_order_id string,
dispatcher_order_type tinyint COMMENT '配送订单类型1.支付单\; 2.退款单',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
circle_master_user_id string COMMENT '圈主ID',
dispatcher_user_id string COMMENT '配送员ID',
dispatcher_order_state tinyint COMMENT '配送订单状态:0.待接单.1.已接单,2.已到店.3.配送中 4.商家普通提货码完成订单.5.商家万能提货码完成订单。6,买家完成订单',
order_goods_num tinyint COMMENT '订单商品的个数',
delivery_fee decimal(36,2) COMMENT '配送员的运费',
distance int COMMENT '配送距离',
dispatcher_code string COMMENT '收货码',
receiver_name string COMMENT '收货人姓名',
receiver_phone string COMMENT '收货人电话',
sender_name string COMMENT '发货人姓名',
sender_phone string COMMENT '发货人电话',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true',
end_date string COMMENT '拉链结束日期')
COMMENT '订单配送详细信息表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--登录记录表(增量表,与ODS一致)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_user_login;
CREATE TABLE bj59_yp_dwd_jiale.fact_user_login(
id string,
login_user string,
login_type string COMMENT '登录类型(登陆时使用)',
client_id string COMMENT '推送标示id(登录、第三方登录、注册、支付回调、给用户推送消息时使用)',
login_time string,
login_ip string,
logout_time string
)
COMMENT '用户登录记录表'
partitioned by (dt string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--购物车(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_shop_cart;
CREATE TABLE bj59_yp_dwd_jiale.fact_shop_cart
(
id string COMMENT '主键id',
shop_store_id string COMMENT '卖家店铺ID',
buyer_id string COMMENT '购买用户ID',
goods_id string COMMENT '购买商品的id',
buy_num INT COMMENT '购买商品的数量',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true',
end_date string COMMENT '拉链结束日期')
comment '购物车'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--收藏店铺记录(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_store_collect;
CREATE TABLE bj59_yp_dwd_jiale.fact_store_collect
(
id string,
user_id string COMMENT '收藏人id',
store_id string COMMENT '店铺id',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
comment '收藏店铺记录表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--商品收藏(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_goods_collect;
CREATE TABLE bj59_yp_dwd_jiale.fact_goods_collect
(
id string,
user_id string COMMENT '收藏人id',
goods_id string COMMENT '商品id',
store_id string COMMENT '通过哪个店铺收藏的(因主店分店概念存在需要)',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
comment '收藏商品记录表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--交易记录(拉链表)
DROP TABLE if exists bj59_yp_dwd_jiale.fact_trade_record;
CREATE TABLE bj59_yp_dwd_jiale.fact_trade_record
(
id string COMMENT '交易单号',
external_trade_no string COMMENT '(支付,结算.退款)第三方交易单号',
relation_id string COMMENT '关联单号',
trade_type TINYINT COMMENT '1.支付订单; 2.结算订单; 3.退款订单;4.充值单;5.提现单;6.分销单;7缴纳保证金单8退还保证金单9,冻结通联订单,10通联通账户余额充值,11.扫码单',
status TINYINT COMMENT '1.成功;2.失败;3.进行中',
finnshed_time string COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完成时间,进行自动收货以及自动评价',
fail_reason string COMMENT '交易失败的原因',
payment_type string COMMENT '支付方式:小程序,app微信,支付宝,快捷支付,钱包,银行卡,消费券',
trade_before_balance DECIMAL(11,2) COMMENT '交易前余额',
trade_true_amount DECIMAL(11,2) COMMENT '交易实际支付金额,第三方平台扣除优惠以后实际支付金额',
trade_after_balance DECIMAL(11,2) COMMENT '交易后余额',
note string COMMENT '业务说明',
user_card string COMMENT '第三方平台账户标识/多钱包用户钱包id',
user_id string COMMENT '用户id',
aip_user_id string COMMENT '钱包id',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid TINYINT COMMENT '是否有效 0: false; 1: true; 订单是否有效的标志',
end_date string COMMENT '拉链结束日期'
)
comment '交易记录'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
9.4.维度表
--区域字典表(全量覆盖)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_district;
CREATE TABLE bj59_yp_dwd_jiale.dim_district(
id string COMMENT '主键ID',
code string COMMENT '区域编码',
name string COMMENT '区域名称',
pid string COMMENT '父级ID',
alias string COMMENT '别名')
COMMENT '区域字典表'
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--时间维度(全量覆盖)
drop table bj59_yp_dwd_jiale.dim_date;
CREATE TABLE bj59_yp_dwd_jiale.dim_date
(
dim_date_id string COMMENT '日期',
date_code string COMMENT '日期编码',
lunar_calendar string COMMENT '农历',
year_code string COMMENT '年code',
year_name string COMMENT '年名称',
month_code string COMMENT '月份编码',
month_name string COMMENT '月份名称',
quanter_code string COMMENT '季度编码',
quanter_name string COMMENT '季度名称',
year_month string COMMENT '年月',
year_week_code string COMMENT '一年中第几周',
year_week_name string COMMENT '一年中第几周名称',
year_week_code_cn string COMMENT '一年中第几周(中国)',
year_week_name_cn string COMMENT '一年中第几周名称(中国',
week_day_code string COMMENT '周几code',
week_day_name string COMMENT '周几名称',
day_week string COMMENT '周',
day_week_cn string COMMENT '周(中国)',
day_week_num string COMMENT '一周第几天',
day_week_num_cn string COMMENT '一周第几天(中国)',
day_month_num string COMMENT '一月第几天',
day_year_num string COMMENT '一年第几天',
date_id_wow string COMMENT '与本周环比的上周日期',
date_id_mom string COMMENT '与本月环比的上月日期',
date_id_wyw string COMMENT '与本周同比的上年日期',
date_id_mym string COMMENT '与本月同比的上年日期',
first_date_id_month string COMMENT '本月第一天日期',
last_date_id_month string COMMENT '本月最后一天日期',
half_year_code string COMMENT '半年code',
half_year_name string COMMENT '半年名称',
season_code string COMMENT '季节编码',
season_name string COMMENT '季节名称',
is_weekend string COMMENT '是否周末(周六和周日)',
official_holiday_code string COMMENT '法定节假日编码',
official_holiday_name string COMMENT '法定节假日',
festival_code string COMMENT '节日编码',
festival_name string COMMENT '节日',
custom_festival_code string COMMENT '自定义节日编码',
custom_festival_name string COMMENT '自定义节日',
update_time string COMMENT '更新时间'
)
COMMENT '时间维度表'
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--店铺(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_store;
CREATE TABLE bj59_yp_dwd_jiale.dim_store(
id string COMMENT '主键',
user_id string,
store_avatar string COMMENT '店铺头像',
address_info string COMMENT '店铺详细地址',
name string COMMENT '店铺名称',
store_phone string COMMENT '联系电话',
province_id int COMMENT '店铺所在省份ID',
city_id int COMMENT '店铺所在城市ID',
area_id int COMMENT '店铺所在县ID',
mb_title_img string COMMENT '手机店铺 页头背景图',
store_description string COMMENT '店铺描述',
notice string COMMENT '店铺公告',
is_pay_bond tinyint COMMENT '是否有交过保证金 1:是0:否',
trade_area_id string COMMENT '归属商圈ID',
delivery_method tinyint COMMENT '配送方式 1 :自提 ;3 :自提加配送均可\; 2 : 商家配送',
origin_price decimal(36,2),
free_price decimal(36,2),
store_type int COMMENT '店铺类型 22天街网店 23实体店 24直营店铺 33会员专区店',
store_label string COMMENT '店铺logo',
search_key string COMMENT '店铺搜索关键字',
end_time string COMMENT '营业结束时间',
start_time string COMMENT '营业开始时间',
operating_status tinyint COMMENT '营业状态 0 :未营业 ;1 :正在营业',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0关闭,1开启,3店铺申请中',
state string COMMENT '可使用的支付类型:MONEY金钱支付\;CASHCOUPON现金券支付',
idcard string COMMENT '身份证',
deposit_amount decimal(36,2) COMMENT '商圈认购费用总额',
delivery_config_id string COMMENT '配送配置表关联ID',
aip_user_id string COMMENT '通联支付标识ID',
search_name string COMMENT '模糊搜索名称字段:名称_+真实名称',
automatic_order tinyint COMMENT '是否开启自动接单功能 1:是 0 :否',
is_primary tinyint COMMENT '是否是总店 1: 是 2: 不是',
parent_store_id string COMMENT '父级店铺的id,只有当is_primary类型为2时有效',
end_date string COMMENT '拉链结束日期')
COMMENT '店铺表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--商圈(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_trade_area;
CREATE TABLE bj59_yp_dwd_jiale.dim_trade_area(
id string COMMENT '主键',
user_id string COMMENT '用户ID',
user_allinpay_id string COMMENT '通联用户表id',
trade_avatar string COMMENT '商圈logo',
name string COMMENT '商圈名称',
notice string COMMENT '商圈公告',
distric_province_id int COMMENT '商圈所在省份ID',
distric_city_id int COMMENT '商圈所在城市ID',
distric_area_id int COMMENT '商圈所在县ID',
address string COMMENT '商圈地址',
radius double COMMENT '半径',
mb_title_img string COMMENT '手机商圈 页头背景图',
deposit_amount decimal(36,2) COMMENT '商圈认购费用总额',
hava_deposit int COMMENT '是否有交过保证金 1:是0:否',
state tinyint COMMENT '申请商圈状态 -1 :未认购 ;0 :申请中;1 :已认购;',
search_key string COMMENT '商圈搜索关键字',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true',
end_date string COMMENT '拉链结束日期')
COMMENT '商圈表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--地址信息表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_location;
CREATE TABLE bj59_yp_dwd_jiale.dim_location(
id string COMMENT '主键',
type int COMMENT '类型 1:商圈地址;2:店铺地址;3.用户地址管理\;4.订单买家地址信息\;5.订单卖家地址信息',
correlation_id string COMMENT '关联表id',
address string COMMENT '地图地址详情',
latitude double COMMENT '纬度',
longitude double COMMENT '经度',
street_number string COMMENT '门牌',
street string COMMENT '街道',
district string COMMENT '区县',
city string COMMENT '城市',
province string COMMENT '省份',
business string COMMENT '百度商圈字段,代表此点所属的商圈',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true',
adcode string COMMENT '百度adcode,对应区县code',
end_date string COMMENT '拉链结束日期')
COMMENT '地址信息'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--商品SKU表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_goods;
CREATE TABLE bj59_yp_dwd_jiale.dim_goods(
id string,
store_id string COMMENT '所属商店ID',
class_id string COMMENT '分类id:只保存最后一层分类id',
store_class_id string COMMENT '店铺分类id',
brand_id string COMMENT '品牌id',
goods_name string COMMENT '商品名称',
goods_specification string COMMENT '商品规格',
search_name string COMMENT '模糊搜索名称字段:名称_+真实名称',
goods_sort int COMMENT '商品排序',
goods_market_price decimal(36,2) COMMENT '商品市场价',
goods_price decimal(36,2) COMMENT '商品销售价格(原价)',
goods_promotion_price decimal(36,2) COMMENT '商品促销价格(售价)',
goods_storage int COMMENT '商品库存',
goods_limit_num int COMMENT '购买限制数量',
goods_unit string COMMENT '计量单位',
goods_state tinyint COMMENT '商品状态 1正常,2下架,3违规(禁售)',
goods_verify tinyint COMMENT '商品审核状态: 1通过,2未通过,3审核中',
activity_type tinyint COMMENT '活动类型:0无活动1促销2秒杀3折扣',
discount int COMMENT '商品折扣(%)',
seckill_begin_time string COMMENT '秒杀开始时间',
seckill_end_time string COMMENT '秒杀结束时间',
seckill_total_pay_num int COMMENT '已秒杀数量',
seckill_total_num int COMMENT '秒杀总数限制',
seckill_price decimal(36,2) COMMENT '秒杀价格',
top_it tinyint COMMENT '商品置顶:1-是,0-否',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
COMMENT '商品表_店铺(SKU)'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--商品分类(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_goods_class;
CREATE TABLE bj59_yp_dwd_jiale.dim_goods_class(
id string,
store_id string COMMENT '店铺id',
class_id string COMMENT '对应的平台分类表id',
name string COMMENT '店铺内分类名字',
parent_id string COMMENT '父id',
level tinyint COMMENT '分类层级',
is_parent_node tinyint COMMENT '是否为父节点:1是0否',
background_img string COMMENT '背景图片',
img string COMMENT '分类图片',
keywords string COMMENT '关键词',
title string COMMENT '搜索标题',
sort int COMMENT '排序',
note string COMMENT '类型描述',
url string COMMENT '分类的链接',
is_use tinyint COMMENT '是否使用:0否,1是',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
COMMENT '商品分类表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
--品牌表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_brand;
CREATE TABLE bj59_yp_dwd_jiale.dim_brand(
id string,
store_id string COMMENT '店铺id',
brand_pt_id string COMMENT '平台品牌库品牌Id',
brand_name string COMMENT '品牌名称',
brand_image string COMMENT '品牌图片',
initial string COMMENT '品牌首字母',
sort int COMMENT '排序',
is_use tinyint COMMENT '0禁用1启用',
goods_state tinyint COMMENT '商品品牌审核状态 1 审核中,2 通过,3 拒绝',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0 :失效,1 :开启',
end_date string COMMENT '拉链结束日期')
COMMENT '品牌(店铺)'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
9.5.完成各个表的数据导入操作
9.5.1全量覆盖的表:时间表
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- hive压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
--分桶
set hive.enforce.bucketing=true;
set hive.enforce.sorting=true;
-- 思考: insert + select 方式导入到目标表, 内部是根据字段名称导入呢? 还是顺序导入呢? 还是都有?
-- 只能按照 顺序导入, 要求 select后查询的结果字段顺序 必须和目标表字段顺序保持一致
-- 但是一般建议, 将顺序和名称都保证一致
-- 首次执行, 和增量执行都是一样的SQL
insert overwrite table bj59_yp_dwd_jiale.dim_date
select
*
from yp_ods.t_date;
9.5.2仅新增同步的表:订单评价表:
-- 仅新增同步表
-- 首次导入的时候, 需要将全部的数据都导入
-- 说明: 在演示中 此处特意加了一个 dt = '2022-04-26' 操作, 主要为了演示后续的增量过程, 实际中首次导入是直接导入全量数据的
insert overwrite table bj59_yp_dwd_jiale.fact_goods_evaluation partition(dt)
select
*
from yp_ods.t_goods_evaluation;
-- 后续的增量导入呢? 每一次导入上一天的新增数据即可
-- 此处使用overwrite, 会不会将历史数据给覆盖掉呢? 对于分区表, 只会覆盖所影响的分区, 不影响的分区不会覆盖, 在进行增量导入的时候, 每一次都是一个新的分区, 所以自然也不会影响原有分区
insert overwrite table bj59_yp_dwd_jiale.fact_goods_evaluation partition(dt)
select
*
from yp_ods.t_goods_evaluation where dt = '2022-04-27';
9.5.3新增及更新同步:t_shop_order和t_store
-- 演示 t_shop_order
-- 由于DWD层拉链表 和 ODS层目标表字段存在差异, 所以无法使用 * 必须将每一个字段全部都得写出来
-- 首次导入: 全量导入
insert overwrite table bj59_yp_dwd_jiale.fact_shop_order partition(start_date)
select
id,
order_num,
buyer_id,
store_id,
case
when order_from = 1 then '安卓'
when order_from = 2 then 'IOS'
when order_from = 3 then '小程序H5'
when order_from = 4 then 'PC'
else null
end as order_from,
order_state,
create_date ,
finnshed_time,
is_settlement,
is_delete ,
evaluation_state ,
way ,
is_stock_up ,
create_user ,
create_time,
update_user ,
update_time ,
is_valid ,
'9999-99-99' as end_date ,
dt as start_date
from yp_ods.t_shop_order;
-- 演示: t_store
-- 首次导入, 全量导入方案
-- 说明: 此处我们只导入了第一次全量数据, 上一天做的增量数据并没有导入, 需要演示后续增量如何实现
insert overwrite table bj59_yp_dwd_jiale.dim_store partition(start_date)
select
id ,
user_id ,
store_avatar,
address_info ,
name ,
store_phone ,
province_id ,
city_id ,
area_id ,
mb_title_img ,
store_description,
notice ,
is_pay_bond ,
trade_area_id ,
delivery_method ,
origin_price ,
free_price ,
store_type ,
store_label ,
search_key,
end_time,
start_time ,
operating_status ,
create_user ,
create_time ,
update_user ,
update_time ,
is_valid ,
state ,
idcard,
deposit_amount ,
delivery_config_id ,
aip_user_id,
search_name,
automatic_order,
is_primary,
parent_store_id ,
'9999-99-99' as end_date ,
dt as start_date
from yp_ods.t_store where dt = '2022-04-26';
9.5.4拉链表的实现操作
店铺表:yp_ods.t_store拉链表实现操作
-- 第一步: 创建店铺表的拉链临时表(此表和目标表是完全一致), 便于后续放置拉链后的结果数据
--店铺临时拉链表(拉链表)
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_store_scd2_temp;
CREATE TABLE bj59_yp_dwd_jiale.dim_store_scd2_temp(
id string COMMENT '主键',
user_id string,
store_avatar string COMMENT '店铺头像',
address_info string COMMENT '店铺详细地址',
name string COMMENT '店铺名称',
store_phone string COMMENT '联系电话',
province_id int COMMENT '店铺所在省份ID',
city_id int COMMENT '店铺所在城市ID',
area_id int COMMENT '店铺所在县ID',
mb_title_img string COMMENT '手机店铺 页头背景图',
store_description string COMMENT '店铺描述',
notice string COMMENT '店铺公告',
is_pay_bond tinyint COMMENT '是否有交过保证金 1:是0:否',
trade_area_id string COMMENT '归属商圈ID',
delivery_method tinyint COMMENT '配送方式 1 :自提 ;3 :自提加配送均可\; 2 : 商家配送',
origin_price decimal(36,2),
free_price decimal(36,2),
store_type int COMMENT '店铺类型 22天街网店 23实体店 24直营店铺 33会员专区店',
store_label string COMMENT '店铺logo',
search_key string COMMENT '店铺搜索关键字',
end_time string COMMENT '营业结束时间',
start_time string COMMENT '营业开始时间',
operating_status tinyint COMMENT '营业状态 0 :未营业 ;1 :正在营业',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0关闭,1开启,3店铺申请中',
state string COMMENT '可使用的支付类型:MONEY金钱支付\;CASHCOUPON现金券支付',
idcard string COMMENT '身份证',
deposit_amount decimal(36,2) COMMENT '商圈认购费用总额',
delivery_config_id string COMMENT '配送配置表关联ID',
aip_user_id string COMMENT '通联支付标识ID',
search_name string COMMENT '模糊搜索名称字段:名称_+真实名称',
automatic_order tinyint COMMENT '是否开启自动接单功能 1:是 0 :否',
is_primary tinyint COMMENT '是否是总店 1: 是 2: 不是',
parent_store_id string COMMENT '父级店铺的id,只有当is_primary类型为2时有效',
end_date string COMMENT '拉链结束日期')
COMMENT '店铺表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
-- 步骤二: 完成拉链表的核心工作 , 将拉链后的数据灌入到临时表中
insert overwrite table bj59_yp_dwd_jiale.dim_store_scd2_temp partition(start_date)
select
A.id ,
A.user_id ,
A.store_avatar,
A.address_info ,
A.name ,
A.store_phone ,
A.province_id ,
A.city_id ,
A.area_id ,
A.mb_title_img ,
A.store_description,
A.notice ,
A.is_pay_bond ,
A.trade_area_id ,
A.delivery_method ,
A.origin_price ,
A.free_price ,
A.store_type ,
A.store_label ,
A.search_key,
A.end_time,
A.start_time ,
A.operating_status ,
A.create_user ,
A.create_time ,
A.update_user ,
A.update_time ,
A.is_valid ,
A.state ,
A.idcard,
A.deposit_amount ,
A.delivery_config_id ,
A.aip_user_id,
A.search_name,
A.automatic_order,
A.is_primary,
A.parent_store_id ,
if(
B.id is null OR A.end_date != '9999-99-99',
A.end_date,
date_add(B.dt, -1)
) as end_date,
A.start_date
from bj59_yp_dwd_jiale.dim_store A left join (select * from yp_ods.t_store where dt = '2022-04-27') B
on A.id = B.id
union all
select
id ,
user_id ,
store_avatar,
address_info ,
name ,
store_phone ,
province_id ,
city_id ,
area_id ,
mb_title_img ,
store_description,
notice ,
is_pay_bond ,
trade_area_id ,
delivery_method ,
origin_price ,
free_price ,
store_type ,
store_label ,
search_key,
end_time,
start_time ,
operating_status ,
create_user ,
create_time ,
update_user ,
update_time ,
is_valid ,
state ,
idcard,
deposit_amount ,
delivery_config_id ,
aip_user_id,
search_name,
automatic_order,
is_primary,
parent_store_id ,
'9999-99-99' as end_date ,
dt as start_date
from yp_ods.t_store where dt = '2022-04-27';
-- 步骤三: 将临时表的数据覆盖写入到目标表
insert overwrite table bj59_yp_dwd_jiale.dim_store partition(start_date)
select
*
from bj59_yp_dwd_jiale.dim_store_scd2_temp;
-- 步骤四: 将临时表数据清空:
DROP TABLE if EXISTS bj59_yp_dwd_jiale.dim_store_scd2_temp;
CREATE TABLE bj59_yp_dwd_jiale.dim_store_scd2_temp(
id string COMMENT '主键',
user_id string,
store_avatar string COMMENT '店铺头像',
address_info string COMMENT '店铺详细地址',
name string COMMENT '店铺名称',
store_phone string COMMENT '联系电话',
province_id int COMMENT '店铺所在省份ID',
city_id int COMMENT '店铺所在城市ID',
area_id int COMMENT '店铺所在县ID',
mb_title_img string COMMENT '手机店铺 页头背景图',
store_description string COMMENT '店铺描述',
notice string COMMENT '店铺公告',
is_pay_bond tinyint COMMENT '是否有交过保证金 1:是0:否',
trade_area_id string COMMENT '归属商圈ID',
delivery_method tinyint COMMENT '配送方式 1 :自提 ;3 :自提加配送均可\; 2 : 商家配送',
origin_price decimal(36,2),
free_price decimal(36,2),
store_type int COMMENT '店铺类型 22天街网店 23实体店 24直营店铺 33会员专区店',
store_label string COMMENT '店铺logo',
search_key string COMMENT '店铺搜索关键字',
end_time string COMMENT '营业结束时间',
start_time string COMMENT '营业开始时间',
operating_status tinyint COMMENT '营业状态 0 :未营业 ;1 :正在营业',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '0关闭,1开启,3店铺申请中',
state string COMMENT '可使用的支付类型:MONEY金钱支付\;CASHCOUPON现金券支付',
idcard string COMMENT '身份证',
deposit_amount decimal(36,2) COMMENT '商圈认购费用总额',
delivery_config_id string COMMENT '配送配置表关联ID',
aip_user_id string COMMENT '通联支付标识ID',
search_name string COMMENT '模糊搜索名称字段:名称_+真实名称',
automatic_order tinyint COMMENT '是否开启自动接单功能 1:是 0 :否',
is_primary tinyint COMMENT '是否是总店 1: 是 2: 不是',
parent_store_id string COMMENT '父级店铺的id,只有当is_primary类型为2时有效',
end_date string COMMENT '拉链结束日期')
COMMENT '店铺表'
partitioned by (start_date string)
row format delimited fields terminated by '\t'
stored as orc
tblproperties ('orc.compress' = 'SNAPPY');
9.5.5额外其他的表处理
其他表仅需要处理全量操作即可,增量不需要操作
事实表:
--增量表,只会新增不会更新
--订单组支付表(增量表,与ODS一致)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_order_pay PARTITION (dt)
SELECT
id
,group_id
,order_pay_amount
,create_date
,create_user
,create_time
,update_user
,update_time
,is_valid
,dt
FROM cy_class3_jiale_yp_ods.t_order_pay;
--用户登录记录表(增量表,与ODS一致)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_user_login PARTITION(dt)
select
id,
login_user,
login_type,
client_id,
login_time,
login_ip,
logout_time,
SUBSTRING(login_time, 1, 10) as dt
FROM yp_ods.t_user_login;
--交易记录(增量表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_trade_record PARTITION (start_date)
SELECT
id,
external_trade_no,
relation_id,
trade_type,
status,
finnshed_time,
fail_reason,
payment_type,
trade_before_balance,
trade_true_amount,
trade_after_balance,
note,
user_card,
user_id,
aip_user_id,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' as end_date
dt as start_date
FROM yp_ods.t_trade_record;
-- 拉链表
--订单事实表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_shop_order PARTITION (start_date)
SELECT
id,
order_num,
buyer_id,
store_id,
case order_from
when 1 then 'android'
when 2 then 'ios'
when 3 then 'miniapp'
when 4 then 'pcweb'
else 'other' end
as order_from,
order_state,
create_date,
finnshed_time,
is_settlement,
is_delete,
evaluation_state,
way,
is_stock_up,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_shop_order
where id is not null and buyer_id is not null and store_id is not null and create_date is not null;
--订单详情表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_shop_order_address_detail PARTITION (start_date)
SELECT
id,
order_amount,
discount_amount,
goods_amount,
is_delivery,
buyer_notes,
pay_time,
receive_time,
delivery_begin_time,
arrive_store_time,
arrive_time,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_shop_order_address_detail;
--商品评价表(拉链表,与ODS一致)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_goods_evaluation_detail PARTITION(start_date)
select
id,
user_id,
store_id,
goods_id,
order_id,
order_goods_id,
geval_scores_goods,
geval_content,
geval_content_superaddition,
geval_addtime,
geval_addtime_superaddition,
geval_state,
geval_remark,
revert_state,
geval_explain,
geval_explain_superaddition,
geval_explaintime,
geval_explaintime_superaddition,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
from yp_ods.t_goods_evaluation_detail;
--订单结算表
set hive.exec.dynamic.partition.mode=nonstrict;
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_order_settle PARTITION (start_date)
SELECT
id
,order_id
,settlement_create_date
,settlement_amount
,dispatcher_user_id
,dispatcher_money
,circle_master_user_id
,circle_master_money
,plat_fee
,store_money
,status
,note
,settle_time
,create_user
,create_time
,update_user
,update_time
,is_valid
,first_commission_user_id
,first_commission_money
,second_commission_user_id
,second_commission_money
,'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_order_settle;
--订单退款表
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_refund_order PARTITION (start_date)
SELECT
id
,order_id
,apply_date
,modify_date
,refund_reason
,refund_amount
,refund_state
,refuse_refund_reason
,refund_goods_type
,refund_shipping_fee
,create_user
,create_time
,update_user
,update_time
,is_valid
,'9999-99-99' end_date
,dt as start_date
FROM yp_ods.t_refund_order;
--订单组表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_shop_order_group PARTITION (start_date)
SELECT
id,
order_id,
group_id,
is_pay,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_shop_order_group;
--订单商品快照(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_shop_order_goods_details PARTITION (start_date)
SELECT
id,
order_id,
shop_store_id,
buyer_id,
goods_id,
buy_num,
goods_price,
total_price,
goods_name,
goods_image,
goods_specification,
goods_weight,
goods_unit,
goods_type,
refund_order_id,
goods_brokerage,
is_refund,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM
yp_ods.t_shop_order_goods_details;
--购物车(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_shop_cart PARTITION (start_date)
SELECT
id,
shop_store_id,
buyer_id,
goods_id,
buy_num,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM
yp_ods.t_shop_cart;
--店铺收藏(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_store_collect PARTITION (start_date)
SELECT
id,
user_id,
store_id,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_store_collect;
--店铺收藏(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_goods_collect PARTITION (start_date)
SELECT
id,
user_id,
goods_id,
store_id,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_goods_collect;
-- 配送表: 拉链表
INSERT overwrite TABLE bj59_yp_dwd_jiale.fact_order_delievery_item PARTITION(start_date)
select
id,
shop_order_id,
refund_order_id,
dispatcher_order_type,
shop_store_id,
buyer_id,
circle_master_user_id,
dispatcher_user_id,
dispatcher_order_state,
order_goods_num,
delivery_fee,
distance,
dispatcher_code,
receiver_name,
receiver_phone,
sender_name,
sender_phone,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' as end_date,
dt as start_date
FROM yp_ods.t_order_delievery_item;
5.6维度表
--全量覆盖
--区域字典表
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_district
select * from yp_ods.t_district
WHERE code IS NOT NULL AND name IS NOT NULL;
--商圈
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_trade_area PARTITION(start_date)
SELECT
id,
user_id,
user_allinpay_id,
trade_avatar,
name,
notice,
distric_province_id,
distric_city_id,
distric_area_id,
address,
radius,
mb_title_img,
deposit_amount,
hava_deposit,
state,
search_key,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_trade_area;
--地址信息表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_location PARTITION(start_date)
SELECT
id,
type,
correlation_id,
address,
latitude,
longitude,
street_number,
street,
district,
city,
province,
business,
create_user,
create_time,
update_user,
update_time,
is_valid,
adcode,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_location;
--商品SKU表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_goods PARTITION(start_date)
SELECT
id,
store_id,
class_id,
store_class_id,
brand_id,
goods_name,
goods_specification,
search_name,
goods_sort,
goods_market_price,
goods_price,
goods_promotion_price,
goods_storage,
goods_limit_num,
goods_unit,
goods_state,
goods_verify,
activity_type,
discount,
seckill_begin_time,
seckill_end_time,
seckill_total_pay_num,
seckill_total_num,
seckill_price,
top_it,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM
yp_ods.t_goods;
--商品分类(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_goods_class PARTITION(start_date)
SELECT
id,
store_id,
class_id,
name,
parent_id,
level,
is_parent_node,
background_img,
img,
keywords,
title,
sort,
note,
url,
is_use,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_goods_class;
--品牌表(拉链表)
INSERT overwrite TABLE bj59_yp_dwd_jiale.dim_brand PARTITION(start_date)
SELECT
id,
store_id,
brand_pt_id,
brand_name,
brand_image,
initial,
sort,
is_use,
goods_state,
create_user,
create_time,
update_user,
update_time,
is_valid,
'9999-99-99' end_date,
dt as start_date
FROM yp_ods.t_brand;
10.Join优化操作
思考:这种reduce端join操作,存在哪些弊端
1.可能会存在数据倾斜的问题(某几个reduce接收数量远远大于其他的reduce接收数据量)
2.所有的数据处理的操作,全部都压在reduce中进行处理,而reduce数量相比map来说少的多,导致整个reduce压力比较大
10.1.Map join
Map Join: 每一个mapTask在读取数据的时候, 每读取一条数据, 就会和内存中班级表数据进行匹配, 如果能匹配的上, 将匹配上数据合并在一起, 输出即可
好处: 将原有reduce join 问题全部都可以解决
弊端:
1- 比较消耗内存
2- 要求整个 Join 中, 必须的都有一个小表, 否则无法放入到内存中
仅适用于: 小表 join 大表 | 大表 join 小表
在老版本(1.x以下)中, 需要将小表放置在前面, 大表放置在后面, 在新版本中, 无所谓
建议, 如果明确知道那些表示小表, 可以优先将这些表, 放置在最前面
如何使用呢?
set hive.auto.convert.join=true; -- 开启 map join的支持 默认值为True
set hive.auto.convert.join.noconditionaltask.size=20971520; -- 设置 小表数据量的最大阈值: 默认值为 20971520(20M)
如果不满足条件, HIVE会自动使用 reduce join 操作
10.2.Bucket Map Join
中型表和大表join:
方案一:如果中型表能对数据进行提前过滤,尽量提前过滤,过滤后,有可能满足了map join条件
方案二:Bucket Map Join
使用条件:
1- Join两个表必须是分桶表
2- 开启 Bucket Map Join 支持: set hive.optimize.bucketmapjoin = true;
3- 一个表的分桶数量是另一个表的分桶数量的整倍数
4- 分桶列 必须 是 join的ON条件的列
5- 必须建立在Map Join场景中
10.3.SMB JOIN
大表和大表join
解决方案:SMB Join
使用条件:
1- 两个表必须都是分桶表
2- 开启 SMB Join 支持:
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
3- 两个表的分桶的数量是一致的
4- 分桶列 必须是 join的 on条件的列, 同时必须保证按照分桶列进行排序操作
-- 开启强制排序
set hive.enforce.sorting=true;
-- 在建分桶表使用: 必须使用sorted by()
5- 应用在Bucket Map Join 场景中
-- 开启 bucket map join
set hive.optimize.bucketmapjoin = true;
6- 必须开启HIVE自动尝试使用SMB 方案:
set hive.optimize.bucketmapjoin.sortedmerge = true;
最终汇总出来整体配置:
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask.size=20971520;
set hive.optimize.bucketmapjoin = true;
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
set hive.enforce.sorting=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
建表:
create table test_smb_2(mid string,age_id string) CLUSTERED BY(mid) SORTED BY(mid) INTO 500 BUCKETS;
至于分多少个桶: 取决于表的数据大小 和 小表阈值 之间相差了多少倍
11.DWB实现
11.1.DWB层作用:维度退化操作
指的将各个维度表或者事实表的核心字段全部汇聚成一个表操作, 形成一个宽表, 这样在后续进行统计分析的时候, 只需要操作这个合并后大宽表数据即可
对于当前项目, 此处的合并宽表过程, 与主题是无关(没有直接关系的), 更多是基于业务模块, 形成业务模块的一些宽表
对于一些其他的项目. 可能从一开始就是直接对主题进行处理, 所以在一些其他的项目中, 可能会直接基于主题形成主题相关的宽表
11.2.创建DWB层的库
-- 1 创建DWB层的库
create database if not exists bj59_yp_dwb_jiale;
11.3订单明细宽表
核心点:
涉及表:
订单事实表: fact_shop_order
订单组表: fact_shop_order_group
订单组支付信息表: fact_order_pay
商品快照表: fact_shop_order_goods_details
订单副表: fact_shop_order_address_detail
退款订单表: fact_refund_order
结算信息表: fact_order_settle
订单评价表: fact_goods_evaluation
订单配送表: fact_order_delievery_item
表与表之间的关系:
订单事实表 -- 订单组表: 订单事实表.id = 订单组表.order_id
订单组表 -- 订单组支付信息表: 订单组表.group_id = 订单组支付信息表.group_id
订单事实表 -- 商品快照表: 订单事实表.id = 商品快照表.order_id
订单事实表 -- 订单副表: 订单事实表.id = 订单副表.id
订单事实表 -- 结算信息表 : 订单事实表.id = 结算信息表.order_id
订单事实表 -- 退款订单表: 订单事实表.id = 退款订单表.order_id
订单事实表 -- 订单评价表: 订单事实表.id = 订单评价表.order_id
订单事实表 -- 订单配送表: 订单事实表.id = 订单配送表.shop_order_id and 订单配送表.dispatcher_order_type = 1
注意: 如果是拉链表, 需要筛选出所有的有效数据
在合并的时候, 每个表都需要那些字段呢?
标准的状态下:
以主表为主要的表,如果多个表之间有相同的字段, 优先使用主表的字段 + 主表中不存在的字段
如果发现其他表中, 有一些字段, 我们后期完全用不上的, 可以在合并宽表的时候, 之间舍弃掉, 如果不确定, 可以保留下来
11.3.1创建订单的目标表
DROP TABLE if EXISTS bj59_yp_dwb_jiale.dwb_order_detail;
CREATE TABLE bj59_yp_dwb_jiale.dwb_order_detail(
order_id string COMMENT '根据一定规则生成的订单编号',
order_num string COMMENT '订单序号',
buyer_id string COMMENT '买家的userId',
store_id string COMMENT '店铺的id',
order_from string COMMENT '渠道类型:android、ios、miniapp、pcweb、other',
order_state int COMMENT '订单状态:1.已下单\; 2.已付款, 3. 已确认 \;4.配送\; 5.已完成\; 6.退款\;7.已 取消',
create_date string COMMENT '下单时间',
finnshed_time timestamp COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据 订单完成时间,进行自动收货以及自动评价',
is_settlement tinyint COMMENT '是否结算\;0.待结算订单\; 1.已结算订单\;',
is_delete tinyint COMMENT '订单评价的状态:0.未删除\; 1.已删除\;(默认0)',
evaluation_state tinyint COMMENT '订单评价的状态:0.未评价\; 1.已评价\;(默认0)',
way string COMMENT '取货方式:SELF自提\;SHOP店铺负责配送',
is_stock_up int COMMENT '是否需要备货 0:不需要 1:需要 2:平台确认备货 3:已完成备货 4平台已经将货 物送至店铺 ',
-- 订单副表
order_amount decimal(36,2) COMMENT '订单总金额:购买总金额-优惠金额',
discount_amount decimal(36,2) COMMENT '优惠金额',
goods_amount decimal(36,2) COMMENT '用户购买的商品的总金额+运费',
is_delivery string COMMENT '0.自提;1.配送',
buyer_notes string COMMENT '买家备注留言',
pay_time string,
receive_time string,
delivery_begin_time string,
arrive_store_time string,
arrive_time string COMMENT '订单完成时间,当配送员点击确认送达时,进行更新订单完成时间,后期需要根据订单完 成时间,进行自动收货以及自动评价',
create_user string,
create_time string,
update_user string,
update_time string,
is_valid tinyint COMMENT '是否有效 0: false\; 1: true\; 订单是否有效的标志',
-- 订单组
group_id string COMMENT '订单分组id',
is_pay tinyint COMMENT '订单组是否已支付,0未支付,1已支付',
-- 订单组支付
group_pay_amount decimal(36,2) COMMENT '订单总金额\;',
-- 退款单
refund_id string COMMENT '退款单号',
apply_date string COMMENT '用户申请退款的时间',
refund_reason string COMMENT '买家退款原因',
refund_amount decimal(36,2) COMMENT '订单退款的金额',
refund_state tinyint COMMENT '1.申请退款\;2.拒绝退款\; 3.同意退款,配送员配送\; 4:商家同意退款,用户亲 自送货 \;5.退款完成',
-- 结算单
settle_id string COMMENT '结算单号',
settlement_amount decimal(36,2) COMMENT '如果发生退款,则结算的金额 = 订单的总金额 - 退款的金额',
dispatcher_user_id string COMMENT '配送员id',
dispatcher_money decimal(36,2) COMMENT '配送员的配送费(配送员的运费(如果退货方式为1:则买家支付配送 费))',
circle_master_user_id string COMMENT '圈主id',
circle_master_money decimal(36,2) COMMENT '圈主分润的金额',
plat_fee decimal(36,2) COMMENT '平台应得的分润',
store_money decimal(36,2) COMMENT '商家应得的订单金额',
status tinyint COMMENT '0.待结算;1.待审核 \; 2.完成结算;3.拒绝结算',
settle_time string COMMENT ' 结算时间',
-- 订单评价
evaluation_id string,
evaluation_user_id string COMMENT '评论人id',
geval_scores int COMMENT '综合评分',
geval_scores_speed int COMMENT '送货速度评分0-5分(配送评分)',
geval_scores_service int COMMENT '服务评分0-5分',
geval_isanony tinyint COMMENT '0-匿名评价,1-非匿名',
evaluation_time string,
-- 订单配送
delievery_id string COMMENT '主键id',
dispatcher_order_state tinyint COMMENT '配送订单状态:0.待接单.1.已接单,2.已到店.3.配送中 4.商家普通 提货码完成订单.5.商家万能提货码完成订单。6,买家完成订单',
delivery_fee decimal(36,2) COMMENT '配送员的运费',
distance int COMMENT '配送距离',
dispatcher_code string COMMENT '收货码',
receiver_name string COMMENT '收货人姓名',
receiver_phone string COMMENT '收货人电话',
sender_name string COMMENT '发货人姓名',
sender_phone string COMMENT '发货人电话',
delievery_create_time string,
-- 商品快照
order_goods_id string COMMENT '--商品快照id',
goods_id string COMMENT '购买商品的id',
buy_num int COMMENT '购买商品的数量',
goods_price decimal(36,2) COMMENT '购买商品的价格',
total_price decimal(36,2) COMMENT '购买商品的价格 = 商品的数量 * 商品的单价 ',
goods_name string COMMENT '商品的名称',
goods_specification string COMMENT '商品规格',
goods_type string COMMENT '商品分类 ytgj:进口商品 ytsc:普通商品 hots爆品',
goods_brokerage decimal(36,2) COMMENT '商家设置的商品分润的金额',
is_goods_refund tinyint COMMENT '0.不退款\; 1.退款'
)
COMMENT '订单明细表'
PARTITIONED BY(dt STRING)
row format delimited fields terminated by '\t' stored as orc tblproperties ('orc.compress' = 'SNAPPY');
11.3.2订单明细宽表
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- hive压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
--分桶
set hive.enforce.bucketing=true;
set hive.enforce.sorting=true;
-- map join 优化操作
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask.size=20971520;
set hive.optimize.bucketmapjoin = true;
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
set hive.enforce.sorting=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
insert overwrite table bj59_yp_dwb_jiale.dwb_order_detail partition(dt)
select
-- 订单事实表
o.id as order_id,
o.order_num,
o.buyer_id,
o.store_id,
o.order_from,
o.order_state,
o.create_date,
o.finnshed_time,
o.is_settlement,
o.is_delete,
o.evaluation_state,
o.way,
o.is_stock_up,
-- 订单副表
ad.order_amount,
ad.discount_amount,
ad.goods_amount,
ad.is_delivery,
ad.buyer_notes,
ad.pay_time,
ad.receive_time,
ad.delivery_begin_time,
ad.arrive_store_time,
ad.arrive_time,
ad.create_user,
ad.create_time,
ad.update_user,
ad.update_time,
ad.is_valid,
-- 订单组表:
g.group_id,
g.is_pay,
-- 订单组支付表
p.order_pay_amount as group_pay_amount,
-- 退款表
r.id as refund_id,
r.apply_date,
r.refund_reason,
r.refund_amount,
r.refund_state,
-- 结算表
s.id as settle_id,
s.settlement_amount,
s.dispatcher_user_id,
s.dispatcher_money,
s.circle_master_user_id,
s.circle_master_money,
s.plat_fee,
s.store_money,
s.status,
s.settle_time,
-- 订单评价表
e.id as evaluation_id,
e.user_id as evaluation_user_id,
e.geval_scores,
e.geval_scores_speed,
e.geval_scores_service,
e.geval_isanony,
e.create_time as evaluation_time,
-- 订单配送表
i.id as delievery_id,
i.dispatcher_order_state,
i.delivery_fee,
i.distance,
i.dispatcher_code,
i.receiver_name,
i.receiver_phone,
i.sender_name,
i.sender_phone,
i.create_time as delievery_create_time,
-- 商品快照
d.id as order_goods_id,
d.goods_id,
d.buy_num,
d.goods_price,
d.total_price,
d.goods_name,
d.goods_specification,
d.goods_type,
d.goods_brokerage,
d.is_refund as is_goods_refund,
substr(o.create_date,1,10) as dt
from (select * from bj59_yp_dwd_jiale.fact_shop_order where end_date = '9999-99-99') o
left join bj59_yp_dwd_jiale.fact_shop_order_group g on o.id = g.order_id and g.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.fact_order_pay p on g.group_id = p.group_id
left join bj59_yp_dwd_jiale.fact_shop_order_goods_details d on o.id = d.order_id and d.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.fact_shop_order_address_detail ad on o.id = ad.id and ad.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.fact_order_settle s on o.id = s.order_id and s.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.fact_refund_order r on o.id = r.order_id and r.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.fact_goods_evaluation e on o.id = e.order_id and e.is_valid = 1
left join bj59_yp_dwd_jiale.fact_order_delievery_item i on o.id = i.shop_order_id and i.dispatcher_order_type = 1 and i.is_valid = 1 and i.end_date = '9999-99-99';
11.4.店铺明细表:
11.4.1创建目标表:
DROP TABLE if EXISTS bj59_yp_dwb_jiale.dwb_shop_detail;
CREATE TABLE bj59_yp_dwb_jiale.dwb_shop_detail(
-- 店铺
id string,
address_info string COMMENT '店铺详细地址',
store_name string COMMENT '店铺名称',
is_pay_bond tinyint COMMENT '是否有交过保证金 1:是0:否',
trade_area_id string COMMENT '归属商圈ID',
delivery_method tinyint COMMENT '配送方式 1 :自提 ;3 :自提加配送均可\; 2 : 商家配送',
store_type int COMMENT '店铺类型 22天街网店 23实体店 24直营店铺 33会员专区店',
is_primary tinyint COMMENT '是否是总店 1: 是 2: 不是',
parent_store_id string COMMENT '父级店铺的id,只有当is_primary类型为2时有效',
-- 商圈
trade_area_name string COMMENT '商圈名称',
-- 区域-店铺
province_id string COMMENT '店铺所在省份ID',
city_id string COMMENT '店铺所在城市ID',
area_id string COMMENT '店铺所在县ID',
province_name string COMMENT '省份名称',
city_name string COMMENT '城市名称',
area_name string COMMENT '县名称'
)
COMMENT '店铺明细表'
PARTITIONED BY(dt STRING)
row format delimited fields terminated by '\t'
stored as orc tblproperties ('orc.compress' = 'SNAPPY');
11.4.2sql实现:
insert overwrite table bj59_yp_dwb_jiale.dwb_goods_detail partition (dt)
select
-- 商品表
g.id ,
g.store_id ,
g.class_id ,
g.store_class_id ,
g.brand_id ,
g.goods_name ,
g.goods_specification ,
g.search_name ,
g.goods_sort ,
g.goods_market_price ,
g.goods_price,
g.goods_promotion_price,
g.goods_storage ,
g.goods_limit_num ,
g.goods_unit ,
g.goods_state ,
g.goods_verify ,
g.activity_type ,
g.discount ,
g.seckill_begin_time ,
g.seckill_end_time ,
g.seckill_total_pay_num,
g.seckill_total_num ,
g.seckill_price,
g.top_it ,
g.create_user ,
g.create_time,
g.update_user,
g.update_time,
g.is_valid ,
-- 商品小类
case
when c1.level = 3 then c1.id
else null end as min_class_id ,
case
when c1.level = 3 then c1.name
else null end as min_class_name ,
-- 商品中类
case
when c1.level = 2 then c1.id
when c2.level = 2 then c2.id
else null end as mid_class_id ,
case
when c1.level = 2 then c1.name
when c2.level = 2 then c2.name
else null end as mid_class_name ,
-- 商品大类
case
when c1.level = 1 then c1.id
when c2.level = 1 then c2.id
when c3.level = 1 then c3.id
else null end as max_class_id ,
case
when c1.level = 1 then c1.name
when c2.level = 1 then c2.name
when c3.level = 1 then c3.name
else null end as max_class_name ,
-- 品牌
b.brand_name ,
substr(g.create_time,1,10) as dt
from (select * from bj59_yp_dwd_jiale.dim_goods where end_date = '9999-99-99') g
left join bj59_yp_dwd_jiale.dim_brand b on g.brand_id = b.id and b.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.dim_goods_class c1 on g.store_class_id = c1.id and c1.end_date ='9999-99-99'
left join bj59_yp_dwd_jiale.dim_goods_class c2 on c1.parent_id = c2.id and c2.end_date = '9999-99-99'
left join bj59_yp_dwd_jiale.dim_goods_class c3 on c2.parent_id = c3.id and c3.end_date = '9999-99-99';
12.HIVE的索引
索引有什么用?用于提升查询的效率
为什么说,索引可以提升查询的效率呢
12.1.Hive的原始索引
hive的原始索引可以针对某个列, 或者某几列构建索引信息, 构建后提升查询执行列的查询效率
存在弊端:
hive原始索引不会自动更新,每次表中数据发生变化后, 都是需要手动重建索引操作, 比较耗费时间和资源, 整体提升性能一般
所以在HIVE3.x版本后, 已经直接将这种索引废弃掉了, 无法使用, 而且官方描述在hive1.x 和 hive2.x版本中, 也不建议优先使用原始索引
12.2.Hive的row group index索引
row group index:行组索引
条件:
1) 要求表的存储类型为ORC存储格式
2) 在创建表的时候, 必须开启 row group index 索引支持
'orc.create.index'='true'
3) 在插入数据的时候, 必须保证需求进行索引列, 按序插入数据
适用于: 数值类型的, 并且对数值类型进行 > < = 操作
思路:
插入数据到ORC表后, 会自动进行划分为多个script片段, 每个片段内部, 会保存着每个字段的最小, 最大值, 这样, 当执行查询 > < = 的条件筛选操作的时候, 根据最小最大值锁定相关的script片段, 从而减少数据扫描量, 提升效率
操作:
CREATE TABLE lxw1234_orc2 (字段列表 ....) stored AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY',
-- 开启行组索引
'orc.create.index'='true'
)
插入数据的时候, 需要保证数据有序的
insert overwrite table lxw1234_orc2
SELECT id, pcid FROM lxw1234_text
-- 插入的数据保持排序(可以使用全局排序, 也可以使用局部排序, 只需要保证一定有序即可, 建议使用局部排序 插入数据效率高一些, 因为全局排序只有一个reduce)
DISTRIBUTE BY id sort BY id;
使用:
set hive.optimize.index.filter=true;
SELECT COUNT(1) FROM lxw1234_orc1 WHERE id >= 1382 AND id <= 1399;
12.3.Hvie的bloom filter index索引
bloom filter index(布隆过滤索引)
条件:
1) 要求表的存储类型为 ORC存储方案
2) 在建表的饿时候, 必须设置为那些列构建布隆索引
3) 仅能适合于等值过滤查询操作
思路:
在开启布隆过滤索引后, 可以针对某个列, 或者某几列来建立索引, 构建索引后, 会将这一列的数据的值存储在对应script片段的索引信息中, 这样当进行 等值查询的时候, 首先会到每一个script片段的索引中, 判断是否有这个值, 如果没有, 直接跳过script, 从而减少数据扫描量, 提升效率
操作:
CREATE TABLE lxw1234_orc2 (字段列表....)
stored AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY',
-- 开启 行组索引 (可选的, 支持全部都打开, 也可以仅开启一个)
'orc.create.index'='true',
-- pcid字段开启BloomFilter索引
'orc.bloom.filter.columns'='pcid,字段2,字段3...'
)
插入数据: 没有要求, 当然如果开启行组索引, 可以将需要使用行组索引的字段, 进行有序插入即可
使用:
SET hive.optimize.index.filter=true;
SELECT COUNT(1) FROM lxw1234_orc1 WHERE id >= 0 AND id <= 1000 AND pcid IN ('0005E26F0DCCDB56F9041C','A');
在什么时候可以使用布隆索引
1- 对于行组索引, 我们建议只要数据存储格式为ORC, 建议将这种索引全部打开, 至于导入数据的时候, 如果能保证有序, 那最好, 如果保证不了, 也无所谓, 大不了这个索引的效率不是特别好
2- 对于布隆过滤索引: 建议将后续会大量的用于等值连接的操作字段, 建立成布隆索引, 比如说: JOIN的字段 经常在where后面出现的等值连接字段
12.4.如何解决数据倾斜的问题
在hive中, 执行一条SQL语句, 最终会被翻译为MR , MR中mapTask和reduceTask都可能存在多个, 数据倾斜主要指的整个MR中reduce阶段有多个,
每个reduce拿到的数据量并不均衡, 导致某一个或者某几个reduce拿到了比其他reduce更多的数据, 导致处理数据压力, 都集中在某几个reduce上, 形成数据倾斜问题, 导致执行时间变长, 影响执行效率
12.5.join数据倾斜
解决方案一:
可以通过 Map Join Bucket Map Join 以及 SMB Join 解决
注意:
通过 Map Join,Bucket Map Join,SMB Join 来解决数据倾斜, 但是 这种操作是存在使用条件的, 如果无法满足这些条件, 无法使用 这种处理方案
解决方案二:
思路: 将那些产生倾斜的key和对应v2的数据, 从当前这个MR中移出去, 单独找一个MR来处理即可, 处理后, 和之前的MR进行汇总结果即可
关键问题: 如何找到那些存在倾斜的key呢? 特点: 这个key数据有很多
运行期处理方案:
思路: 在执行MR的时候, 会动态统计每一个 k2的值出现重复的次数, 当这个重复的次数达到一定的阈值后, 认为当前这个k2的数据存在数据倾斜, 自动将其剔除, 交由给一个单独的MR来处理即可,两个MR处理完成后, 将结果基于union all 合并在一起即可
实操:
set hive.optimize.skewjoin=true; -- 开启运行期处理倾斜参数
set hive.skewjoin.key=100000; -- 阈值, 此参数在实际生产环境中, 需要调整在一个合理的值(否则极易导致大量的key都是倾斜的)
判断依据: 查看 join的 字段 对应重复的数量有多少个, 然后选择一个合理值
比如判断: id为 1 大概有 100w id为 2 88w id 为 3 大概有 500w 设置阈值为 大于500w次数据
或者: 总数量大量1000w, 然后共有 1000个班级, 平均下来每个班级数量大概在 1w条, 设置阈值: 大于 3w条 ~5w条范围 (超过3~5倍才认为倾斜)
适用于: 并不清楚那个key容易产生倾斜, 此时交由系统来动态检测
编译期处理方案:
思路: 在创建这个表的时候, 我们就可以预知到后续插入到这个表中数据, 那些key的值会产生倾斜, 在建表的时候, 将其提前配置设置好即可, 在后续运行的时候, 程序会自动将设置的key的数据单独找一个MR来进行处理即可, 处理完成后, 再和原有结果进行union all 合并操作
实操:
set hive.optimize.skewjoin.compiletime=true; -- 开启编译期处理倾斜参数
CREATE TABLE list_bucket_single (key STRING, value STRING)
-- 倾斜的字段和需要拆分的key值
SKEWED BY (key) ON (1,5,6)
-- 为倾斜值创建子目录单独存放
[STORED AS DIRECTORIES];
适用于: 提前知道那些key存在倾斜
在实际生产环境中, 应该使用那种方式呢? 两种方式都会使用的
一般来说, 会将两个都开启, 编译期的明确在编译期将其设置好, 编译期不清楚, 通过运行期动态捕获即可
union all 优化方案:
说明: 不管是运行期 还是编译期的join倾斜解决, 最终都会运行多个MR, 将多个MR结果通过union all 进行汇总, union all也是需要单独一个MR来处理
解决方案:
让每一个MR在运行完成后, 直接将结果输出到目的地即可, 默认 是各个MR将结果输出临时目录, 通过 union all 合并到最终目的地
开启此参数即可:
set hive.optimize.union.remove=true;
12.6.group by数据倾斜
假设目前有这么一个表:
sid sname cid
s01 张三 c01
s02 李四 c02
s03 王五 c01
s04 赵六 c03
s05 田七 c02
s06 周八 c01
s07 李九 c01
s08 老王 c04
需求: 请计算每个班级有多少个人
select cid,count(1) as total from stu group by cid;
翻译后MR是如何处理SQL呢?
MAP 阶段: 假设Map阶段跑了二个MapTask
mapTask1:
k2 v2
c01 {s01 张三 c01}
c02 {s02 李四 c02}
c01 {s03 王五 c01}
c03 {s04 赵六 c03}
mapTask2:
k2 v2
c02 {s05 田七 c02}
c01 {s06 周八 c01}
c01 {s07 李九 c01}
c04 {s08 老王 c04}
reduce阶段: 假设reduceTask有二个
reduceTask1: 接收 c01 和 c02的数据
接收数据
k2 v2
c01 {s01 张三 c01}
c02 {s02 李四 c02}
c01 {s03 王五 c01}
c02 {s05 田七 c02}
c01 {s06 周八 c01}
c01 {s07 李九 c01}
分组后:
c01 [{s01 张三 c01},{s03 王五 c01},{s06 周八 c01},{s07 李九 c01}]
c02 [{s02 李四 c02},{s05 田七 c02}]
结果数据:
c01 4
c02 2
reduceTask2: 接收 c03 和 c04的数据
接收数据
k2 v2
c03 {s04 赵六 c03}
c04 {s08 老王 c04}
分组后:
c03 [{s04 赵六 c03}]
c04 [{s08 老王 c04}]
结果数据:
c03 1
c04 1
在以上整个计算流程中, 发现 其中一个reduce接收到的数据量比另一个reduce接收的数据量要多的多, 认为出现了数据倾斜的问题, 所以group by 也有可能产生数据倾斜
思考:如何解决group by的数据倾斜
解决方案一:基于mr的conbiner(规约,提前聚合)减少数据达到reduce数量,从而减轻倾斜问题
假设目前有这么一个表:
sid sname cid
s01 张三 c01
s02 李四 c02
s03 王五 c01
s04 赵六 c03
s05 田七 c02
s06 周八 c01
s07 李九 c01
s08 老王 c04
需求: 请计算每个班级有多少个人
select cid,count(1) as total from stu group by cid;
翻译后MR是如何处理SQL呢?
MAP 阶段: 假设Map阶段跑了二个MapTask
mapTask1:
k2 v2
c01 {s01 张三 c01}
c02 {s02 李四 c02}
c01 {s03 王五 c01}
c03 {s04 赵六 c03}
规约(提前聚合)操作: 处理逻辑与reduce处理逻辑一直
分组:
c01 [{s01 张三 c01},{s03 王五 c01}]
c02 [{s02 李四 c02}]
c03 [{s04 赵六 c03}]
聚合得出结果:
c01 2
c02 1
c03 1
mapTask2:
k2 v2
c02 {s05 田七 c02}
c01 {s06 周八 c01}
c01 {s07 李九 c01}
c04 {s08 老王 c04}
规约(提前聚合)操作: 处理逻辑与reduce处理逻辑一直
分组:
c01 [{s06 周八 c01},{s07 李九 c01}]
c02 [{s05 田七 c02}]
c04 [{s08 老王 c04}]
聚合得出结果:
c01 2
c02 1
c04 1
reduce阶段: 假设reduceTask有二个
reduceTask1: 接收 c01 和 c02的数据
接收数据
k2 v2
c01 2
c02 1
c01 2
c02 1
分组后:
c01 [2,2]
c02 [1,1]
结果数据:
c01 4
c02 2
reduceTask2: 接收 c03 和 c04的数据
接收数据
k2 v2
c03 1
c04 1
分组后:
c03 [1]
c04 [1]
结果数据:
c03 1
c04 1
通过规约来解决数据倾斜, 处理完成后, 发现 两个reduce中从原来相差 3倍, 变更为相差 2倍, 减轻了数据倾斜问题
如何配置呢?
只需要在HIVE中开启combiner提前聚合配置参数即可:
set hive.map.aggr=true;
方案二:负载均衡的解决方案(需要运行两个mr来处理)
假设目前有这么一个表:
sid sname cid
s01 张三 c01
s02 李四 c02
s03 王五 c01
s04 赵六 c03
s05 田七 c02
s06 周八 c01
s07 李九 c01
s08 老王 c04
需求: 请计算每个班级有多少个人
select cid,count(1) as total from stu group by cid;
翻译后MR是如何处理SQL呢?
第一个MR的操作: 对数据进行打散
Map 阶段: 假设运行了两个MapTask
mapTask1:
k2 v2
c01 {s01 张三 c01}
c02 {s02 李四 c02}
c01 {s03 王五 c01}
c03 {s04 赵六 c03}
mapTask2:
k2 v2
c02 {s05 田七 c02}
c01 {s06 周八 c01}
c01 {s07 李九 c01}
c04 {s08 老王 c04}
mapTask执行完成后, 在进行分发数据到达reduce, 默认情况下将相同k2的数据发往同一个reduce, 目前采用防范为随机分发, 保证每一个reduce拿到相等数量的数据信息(负载过程, 让每一个reduce接收到相同数量的数据)
reduce阶段: 假设有两个reduceTask
reduceTask1:
接收到数据:
c01 {s01 张三 c01}
c02 {s02 李四 c02}
c01 {s03 王五 c01}
c01 {s06 周八 c01}
分组操作:
c01 [{s01 张三 c01},{s03 王五 c01},{s06 周八 c01}]
c02 [{s02 李四 c02}]
输出结果:
c01 3
c02 1
reduceTask2:
接收到数据:
c03 {s04 赵六 c03}
c02 {s05 田七 c02}
c01 {s07 李九 c01}
c04 {s08 老王 c04}
分组操作:
c03 [{s04 赵六 c03}]
c02 [{s05 田七 c02}]
c01 [{s07 李九 c01}]
c04 [{s08 老王 c04}]
输出结果:
c01 1
c02 1
c03 1
c04 1
第一个MR执行完成了, 每个reduce都接收到四条数据, 自然也就不存在数据倾斜的问题了
第二个MR进行处理: 严格按照相同k2发往同一个reduce
Map 阶段: 假设有二个mapTask
mapTask1:
k2 v2
c01 3
c01 1
c02 1
mapTask2:
k2 v2
c02 1
c03 1
c04 1
reduce阶段: 假设有两个reduce
reduceTask1: 接收 c01 和 c02 数据
接收数据:
k2 v2
c01 3
c01 1
c02 1
c02 1
结果:
c01 4
c02 2
reduceTask2: 接收 c03 和c04
接收数据:
k2 v2
c03 1
c04 1
结果:
c03 1
c04 1
通过负载均衡方式来解决数据倾斜, 同样也可以减轻数据倾斜的压力
细细发现, 方案一 和 方案二, 是有类似之处的, 方案一, 让每一个mapTask内部进行提前聚合, 然后到达reduce进行汇总合并得出结构, 方案二: 让第一个MR进行打散并对数据进行聚合计算 得出局部结果, 然后让第二个MR进行最终聚合计算操作, 得出最终结果
说明: 方案二, 比方案一, 更能彻底解决数据倾斜问题, 因为其处理数据范围更大, 整个整个数据集来处理, 而方案一, 只是每个MapTask处理, 仅仅局部处理
如何使用方案二:
只需要开启负载均衡的HIVE参数配置即可:
set hive.groupby.skewindata=true;
这两种方式: 建议在生产中, 优先使用第一种, 如果第一种无法解决, 尝试使用第二种解决
注意事项: 使用第二种负载均衡的解决group by 的数据倾斜, 一定要注意, SQL语句中不能出现多次 distinct操作, 否则 HIVE会直接报错的
错误信息:
Error in semantic analysis: DISTINCT on different columns not supported with skew in data.
比如说:
SELECT ip, count(DISTINCT uid), count(DISTINCT uname) FROMlog GROUP BY ip 此操作就直接报错了,只能使用方案一解决数据倾斜
倾斜的参数配置开启条件,一定是出现了数据倾斜的问题,如果没有出现,不需要开启的
如何才能知道数据发生了倾斜
倾斜发生后, 出现的问题, 程序迟迟无法结束, 或者说翻译的MR中reduceTask有多个, 大部分的reduceTask都执行完成了, 只有其中一个或者几个没有执行完成, 此时认为发生了数据倾斜
12.7 关键点: 如何查看每一个reduceTask执行时间
方式一: 通过Yarn查看(运行过程中) 或者 jobhistory查看(已经结束的程序) (此操作, 只能在本地演示查看, 云端环境没有开启yarn端口, 无法查看的)
目前, 我们这里可能只有一个reduce, 但是实际上生产环境中, 此位置可能会有多个reduceTask, 我们需要观察每个reduceTask执行时间, 如果发现其中一个或者几个reduce执行时间, 远远大于其他的reduceTask执行时间, 那么说明存在数据倾斜的问题
点击reduce进入:
方案二:通过HUE方式也可以查看
13.DWS实现
DWS层: 业务层
基于主题统计分析, 此层一般适用于进行细化粒度的聚合统计操作, 主要为了服务后续上卷统计过程 (提前聚合操作)
比如说:
以年 月 日 来统计操作, 在DWS层, 仅需要按照 日进行统计相关的指标即可, 进行提前聚合操作
后续在DM层, 进行上卷操作, 将 月 和 年 基于日统计宽表 计算得出
注意:
如果要进行提前聚合操作, 不能对数据进行去重统计 ,
比如说: 要统计用户量, 分别统计 每天 每月 每年的数据
对于用户, 可能今天购买, 明天也会, 这个月会购买, 下个月也会购买
如果先按照每天统计用户量, 得出一个结果, 比如说
2022-05-01: 用户量 100
2022-05-02: 用户量 160
此时统计五月份的数据: 如果直接将 100 + 160 = 260 用户 这是错误, 可能实际只有 180个用户
所以说: 如果需要去重计算指标, 不管计算日 月 年, 都需要针对原始数据来计算处理, 不能提前聚合
本次DWS层, 共计有三个主题需要进行统计: 销售主题, 商品主题, 和 用户主题, 在实际面试中, 可以只负责其中一个主题或者二个主题即可, 无需全部负责, 但是学习中, 希望三个主题都能全部搞定
对于DWS层以前的层次, 仅负责其中一个业务模块或者二个业务模块即可
13.1 销售主题的日统计宽表
可分析的主要指标有:销售收入、平台收入、配送成交额、小程序成交额、安卓APP成交额、 苹果APP成交额、PC商城成交额、订单量、参评单量、差评单量、配送单量、退款单量、小程序订单量、安卓APP订单量、苹果APP订单量、PC商城订单量。
维度有:日期、城市、商圈、店铺、品牌、大类、中类、小类
维度组合:
日期: 日
日期 + 城市
日期 + 城市 + 商圈
日期 + 城市 + 商圈 + 店铺
日期 + 品牌
日期 + 大类
日期 + 大类 + 中类
日期 + 大类 + 中类 + 小类
16 * 8 = 128 个需求指标结果
分析, 当前需求统计的这些维度 和 指标, 需要涉及到那些表, 以及涉及到那些字段呢?
维度字段:
日期: dwb_order_detail.dt
城市: dwb_shop_detail: city_id 和 city_name
商圈: dwb_shop_detail: trade_area_id 和 trade_area_name
店铺: dwb_shop_detail: id 和 store_name
品牌: dwb_goods_detail: brand_id 和 brand_name
大类: dwb_goods_detail: max_class_id 和 max_class_name
中类: dwb_goods_detail: mid_class_id 和 mid_class_name
小类: dwb_goods_detail: min_class_id 和 min_class_name
指标字段:
订单量相关指标: dwb_order_detail.order_id
订单销售收入(销售收入, 小程序, 安卓, 苹果, pc端):dwb_order_detail.order_amount
平台收入: dwb_order_detail.plat_fee
配送费: wb_order_detail.delivery_fee
涉及表:
订单明细宽表(当前主题的事实表): dwb_order_detail (事实表)
店铺明细宽表: dwb_shop_detail (维度表)
商品明细宽表: dwb_goods_detail (维度表)
关联条件:
订单表 和 店铺表:
订单明细宽表.store_id = 店铺明细宽表.id
订单表 和 商品表:
订单明细宽表.goods_id = 商品明细宽表.id
思考: 当前这个是三种数仓模型那一种呢? 星型模型
是否需要过滤一些操作呢?
1- 保证必须是支付状态: is_pay = 1
2- 保证订单状态: order_state 不能是 1(已下单, 没有付款) 和 7 (已取消)
13.2使用DBeaver连接Hive
13.3创建DWS库和表(销售主题日统计宽表)
1.创建表
– 创建库:
create database if not exists bj59_yp_dws_jiale;
-- 创建表(指标字段 + 维度字段 + 经验字段 ):
DROP TABLE IF EXISTS bj59_yp_dws_jiale.dws_sale_daycount;
CREATE TABLE bj59_yp_dws_jiale.dws_sale_daycount(
--dt STRING,
province_id string COMMENT '省份id',
province_name string COMMENT '省份名称',
city_id string COMMENT '城市id',
city_name string COMMENT '城市name',
trade_area_id string COMMENT '商圈id',
trade_area_name string COMMENT '商圈名称',
store_id string COMMENT '店铺的id',
store_name string COMMENT '店铺名称',
brand_id string COMMENT '品牌id',
brand_name string COMMENT '品牌名称',
max_class_id string COMMENT '商品大类id',
max_class_name string COMMENT '大类名称',
mid_class_id string COMMENT '中类id',
mid_class_name string COMMENT '中类名称',
min_class_id string COMMENT '小类id',
min_class_name string COMMENT '小类名称',
-- 用于标记数据结果是按照那个维度来统计的一个经验字段
group_type string COMMENT '分组类型:store,trade_area,city,brand,min_class,mid_class,max_class, all',
-- =======日统计=======
-- 销售收入
sale_amt DECIMAL(38,2) COMMENT '销售收入',
-- 平台收入
plat_amt DECIMAL(38,2) COMMENT '平台收入',
-- 配送成交额
deliver_sale_amt DECIMAL(38,2) COMMENT '配送成交额',
-- 小程序成交额
mini_app_sale_amt DECIMAL(38,2) COMMENT '小程序成交额',
-- 安卓APP成交额
android_sale_amt DECIMAL(38,2) COMMENT '安卓APP成交额',
-- 苹果APP成交额
ios_sale_amt DECIMAL(38,2) COMMENT '苹果APP成交额',
-- PC商城成交额
pcweb_sale_amt DECIMAL(38,2) COMMENT 'PC商城成交额',
-- 成交单量
order_cnt BIGINT COMMENT '成交单量',
-- 参评单量
eva_order_cnt BIGINT COMMENT '参评单量comment=>cmt',
-- 差评单量
bad_eva_order_cnt BIGINT COMMENT '差评单量negtive-comment=>ncmt',
-- 配送成交单量
deliver_order_cnt BIGINT COMMENT '配送单量',
-- 退款单量
refund_order_cnt BIGINT COMMENT '退款单量',
-- 小程序成交单量
miniapp_order_cnt BIGINT COMMENT '小程序成交单量',
-- 安卓APP订单量
android_order_cnt BIGINT COMMENT '安卓APP订单量',
-- 苹果APP订单量
ios_order_cnt BIGINT COMMENT '苹果APP订单量',
-- PC商城成交单量
pcweb_order_cnt BIGINT COMMENT 'PC商城成交单量'
)
COMMENT '销售主题日统计宽表'
PARTITIONED BY(dt STRING)
ROW format delimited fields terminated BY '\t'
stored AS orc tblproperties ('orc.compress' = 'SNAPPY');
根据 日期 + 城市, 统计相关的指标:
2.sql实现:
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- hive压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
-- map join 优化操作
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask.size=20971520;
-- group by 数据倾斜的优化
-- set hive.map.aggr=true; -- 方案一
set hive.groupby.skewindata=true; -- 方案二
-- 日期 + 城市
-- 第一步: 先去重操作
with t1 as (
select
--维度字段:
o.dt,
s.province_id,
s.province_name,
s.city_id,
s.city_name,
-- 指标字段
o.order_id , -- 订单ID, 计算订单量相关指标
o.order_amount, -- 订单额, 计算订单销售收入相关指标
o.plat_fee, -- 平台分润
o.delivery_fee, -- 配送费用
-- 判断字段,
o.order_from, -- 渠道类型
o.evaluation_id, -- 评价ID, 用于判断是否参评
o.geval_scores, -- 综合评分
o.delievery_id, -- 配送ID, 用于判断是否配送
o.refund_id, -- 退款ID, 用于判断是否退款单
-- 去重操作处理逻辑:
row_number() over(partition by o.order_id) as rn
from bj59_yp_dwb_jiale.dwb_order_detail o
left join bj59_yp_dwb_jiale.dwb_shop_detail s on o.store_id = s.id
left join bj59_yp_dwb_jiale.dwb_goods_detail g on o.goods_id = g.id
-- 确保订单是支付状态
where o.is_pay = 1 and o.order_state not in(1,7)
)
insert overwrite table bj59_yp_dws_jiale.dws_sale_daycount partition(dt)
select
province_id,
province_name,
city_id,
city_name,
'-1' as trade_area_id,
'-1' as trade_area_name ,
'-1' as store_id ,
'-1' as store_name ,
'-1' as brand_id ,
'-1' as brand_name ,
'-1' as max_class_id ,
'-1' as max_class_name ,
'-1' as mid_class_id ,
'-1' as mid_class_name ,
'-1' as min_class_id ,
'-1' as min_class_name ,
'city' as group_type,
-- --------------以下为指标统计操作---------------------
-- coalesce:(字段1,字段2....): 用于返回第一个不为null的值
sum(coalesce(order_amount,0)) as sale_amt,
sum(coalesce(plat_fee,0)) as plat_amt, -- 平台收入
sum(coalesce(delivery_fee,0)) as deliver_sale_amt, -- 配送成交额
sum(
if(
order_from = 'miniapp',
coalesce(order_amount,0),
0
)
) as mini_app_sale_amt, -- 小程序成交额
sum(
if(
order_from = 'android',
coalesce(order_amount,0),
0
)
) as android_sale_amt, -- Android成交额
sum(
if(
order_from = 'ios',
coalesce(order_amount,0),
0
)
) as ios_sale_amt, -- IOS成交额
sum(
if(
order_from = 'pcweb',
coalesce(order_amount,0),
0
)
) as pcweb_sale_amt, -- pc商城成交额
count(order_id) as order_cnt , -- 成交单量
-- 参评单量: 订单被评价了, 就说明这个订单属于参评订单了, 需要进行统计
count(
if(
evaluation_id is not null ,
order_id,
NULL
)
) as eva_order_cnt, -- 参评单量
-- 差评单量: 必须评价, 而且呢, 评价分数比较低
count(
if(
evaluation_id is not null and geval_scores is not null and geval_scores <=6,
order_id,
NULL
)
) as bad_eva_order_cnt,
-- 配送成交单量: 订单必须是已经被配送的, 如果配送,属于配送单
count(
if(
delievery_id is not null,
order_id,
null
)
) as deliver_order_cnt, -- 配送成交单量
-- 退款单量: 如果说退款ID存在, 说明订单一定是退款单
count(
if(
refund_id is not null,
order_id,
null
)
) as refund_order_cnt,
count(
if(
order_from = 'miniapp',
order_id,
NULL
)
) as mini_app_order_cnt, -- 小程序订单量
count(
if(
order_from = 'android',
order_id,
NULL
)
) as android_order_cnt, -- Android订单量
count(
if(
order_from = 'ios',
order_id,
NULL
)
) as ios_order_cnt, -- IOS订单量
count(
if(
order_from = 'pcweb',
order_id,
NULL
)
) as pcweb_order_cnt, -- pc商城订单量
dt
from t1 where rn = 1
group by dt, province_id,province_name,city_id,city_name;
日期+城市+商圈(与上一个类型):
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- hive压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
-- map join 优化操作
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask.size=20971520;
-- group by 数据倾斜的优化
-- set hive.map.aggr=true; -- 方案一
set hive.groupby.skewindata=true; -- 方案二
-- 日期 + 城市
-- 第一步: 先去重操作
with t1 as (
select
--维度字段:
o.dt,
s.province_id,
s.province_name,
s.city_id,
s.city_name,
s.trade_area_id,
s.trade_area_name,
-- 指标字段
o.order_id , -- 订单ID, 计算订单量相关指标
o.order_amount, -- 订单额, 计算订单销售收入相关指标
o.plat_fee, -- 平台分润
o.delivery_fee, -- 配送费用
-- 判断字段,
o.order_from, -- 渠道类型
o.evaluation_id, -- 评价ID, 用于判断是否参评
o.geval_scores, -- 综合评分
o.delievery_id, -- 配送ID, 用于判断是否配送
o.refund_id, -- 退款ID, 用于判断是否退款单
-- 去重操作处理逻辑:
row_number() over(partition by o.order_id) as rn
from bj59_yp_dwb_jiale.dwb_order_detail o
left join bj59_yp_dwb_jiale.dwb_shop_detail s on o.store_id = s.id
left join bj59_yp_dwb_jiale.dwb_goods_detail g on o.goods_id = g.id
-- 确保订单是支付状态
where o.is_pay = 1 and o.order_state not in(1,7)
)
insert into table bj59_yp_dws_jiale.dws_sale_daycount partition(dt)
select
province_id,
province_name,
city_id,
city_name,
trade_area_id,
trade_area_name ,
'-1' as store_id ,
'-1' as store_name ,
'-1' as brand_id ,
'-1' as brand_name ,
'-1' as max_class_id ,
'-1' as max_class_name ,
'-1' as mid_class_id ,
'-1' as mid_class_name ,
'-1' as min_class_id ,
'-1' as min_class_name ,
'trade_area' as group_type,
-- --------------以下为指标统计操作---------------------
-- coalesce:(字段1,字段2....): 用于返回第一个不为null的值
sum(coalesce(order_amount,0)) as sale_amt,
sum(coalesce(plat_fee,0)) as plat_amt, -- 平台收入
sum(coalesce(delivery_fee,0)) as deliver_sale_amt, -- 配送成交额
sum(
if(
order_from = 'miniapp',
coalesce(order_amount,0),
0
)
) as mini_app_sale_amt, -- 小程序成交额
sum(
if(
order_from = 'android',
coalesce(order_amount,0),
0
)
) as android_sale_amt, -- Android成交额
sum(
if(
order_from = 'ios',
coalesce(order_amount,0),
0
)
) as ios_sale_amt, -- IOS成交额
sum(
if(
order_from = 'pcweb',
coalesce(order_amount,0),
0
)
) as pcweb_sale_amt, -- pc商城成交额
count(order_id) as order_cnt , -- 成交单量
-- 参评单量: 订单被评价了, 就说明这个订单属于参评订单了, 需要进行统计
count(
if(
evaluation_id is not null ,
order_id,
NULL
)
) as eva_order_cnt, -- 参评单量
-- 差评单量: 必须评价, 而且呢, 评价分数比较低
count(
if(
evaluation_id is not null and geval_scores is not null and geval_scores <=6,
order_id,
NULL
)
) as bad_eva_order_cnt,
-- 配送成交单量: 订单必须是已经被配送的, 如果配送,属于配送单
count(
if(
delievery_id is not null,
order_id,
null
)
) as deliver_order_cnt, -- 配送成交单量
-- 退款单量: 如果说退款ID存在, 说明订单一定是退款单
count(
if(
refund_id is not null,
order_id,
null
)
) as refund_order_cnt,
count(
if(
order_from = 'miniapp',
order_id,
NULL
)
) as mini_app_order_cnt, -- 小程序订单量
count(
if(
order_from = 'android',
order_id,
NULL
)
) as android_order_cnt, -- Android订单量
count(
if(
order_from = 'ios',
order_id,
NULL
)
) as ios_order_cnt, -- IOS订单量
count(
if(
order_from = 'pcweb',
order_id,
NULL
)
) as pcweb_order_cnt, -- pc商城订单量
dt
from t1 where rn = 1
group by dt, province_id,province_name,city_id,city_name,trade_area_id,trade_area_name;
● 日期 + 品牌 : (需求思考, 处理逻辑是不一样的, 因为 一个订单中可以有多个品牌, 一个品牌可以有多个订单)
○ 目前订单明细宽表存在问题: 一个子订单中 可能存在重复的数据信息
订单和品牌之间的关系: 一个订单下, 可以多个品牌的数据, 一个品牌也可以有多个订单数据, 之间的关系为 多对多
-- 开启动态分区支持:
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=10000;
set hive.exec.max.dynamic.partitions=100000;
set hive.exec.max.created.files=150000;
-- hive压缩
set hive.exec.compress.intermediate=true;
set hive.exec.compress.output=true;
-- 写入时压缩生效
set hive.exec.orc.compression.strategy=COMPRESSION;
-- map join 优化操作
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask.size=20971520;
-- group by 数据倾斜的优化
set hive.map.aggr=true; -- 方案一
set hive.groupby.skewindata=false; -- 方案二
-- 第一步: 先去重操作
with t1 as (
select
--维度字段:
o.dt,
s.province_id,
s.province_name,
s.city_id,
s.city_name,
s.trade_area_id,
s.trade_area_name,
g.brand_id,
g.brand_name,
-- 指标字段
o.order_id , -- 订单ID, 计算订单量相关指标
o.order_amount, -- 订单额, 计算订单销售收入相关指标
o.plat_fee, -- 平台分润
o.delivery_fee, -- 配送费用
o.total_price, -- 商品的价格, 在计算 基于品牌 大类 中类 小类
-- 判断字段,
o.order_from, -- 渠道类型
o.evaluation_id, -- 评价ID, 用于判断是否参评
o.geval_scores, -- 综合评分
o.delievery_id, -- 配送ID, 用于判断是否配送
o.refund_id, -- 退款ID, 用于判断是否退款单
-- 去重操作处理逻辑:
row_number() over(partition by o.order_id) as rn, -- 计算 日期, 日期+城市 , 日期+城市+商圈, 日期+城市+商圈+店铺
row_number() over(partition by o.order_id, o.goods_id,g.brand_id) as rn1 -- 日期+品牌
from bj59_yp_dwb_jiale.dwb_order_detail o
left join bj59_yp_dwb_jiale.dwb_shop_detail s on o.store_id = s.id
left join bj59_yp_dwb_jiale.dwb_goods_detail g on o.goods_id = g.id
-- 确保订单是支付状态
where o.is_pay = 1 and o.order_state not in(1,7)
)
insert into table bj59_yp_dws_jiale.dws_sale_daycount partition(dt)
select
-- 书写维度信息字段:
'-1' as province_id,
'-1' as province_name,
'-1' as city_id,
'-1' as city_name,
'-1' as trade_area_id,
'-1' as trade_area_name ,
'-1' as store_id ,
'-1' as store_name ,
brand_id ,
brand_name ,
'-1' as max_class_id ,
'-1' as max_class_name ,
'-1' as mid_class_id ,
'-1' as mid_class_name ,
'-1' as min_class_id ,
'-1' as min_class_name ,
'brand' as group_type,
-- 指标计算操作
sum(coalesce(total_price,0)) as sale_amt, -- 销售收入
null as plat_amt, -- 平台收入(无法计算,因为都是基于订单, 而不是基于品牌)
null as deliver_sale_amt, -- 配送成交额(无法计算,因为都是基于订单, 而不是基于品牌)
sum(
if(
order_from = 'miniapp',
coalesce(total_price,0),
0
)
) as mini_app_sale_amt, -- 小程序成交额
sum(
if(
order_from = 'android',
coalesce(total_price,0),
0
)
) as android_sale_amt, -- Android成交额
sum(
if(
order_from = 'ios',
coalesce(total_price,0),
0
)
) as ios_sale_amt, -- IOS成交额
sum(
if(
order_from = 'pcweb',
coalesce(total_price,0),
0
)
) as pcweb_sale_amt, -- pc商城成交额
count(distinct order_id) as order_cnt , -- 成交单量
-- 参评单量: 订单被评价了, 就说明这个订单属于参评订单了, 需要进行统计
count( distinct
if(
evaluation_id is not null ,
order_id,
NULL
)
) as eva_order_cnt, -- 参评单量
-- 差评单量: 必须评价, 而且呢, 评价分数比较低
count( distinct
if(
evaluation_id is not null and geval_scores is not null and geval_scores <=6,
order_id,
NULL
)
) as bad_eva_order_cnt,
-- 配送成交单量: 订单必须是已经被配送的, 如果配送,属于配送单
count( distinct
if(
delievery_id is not null,
order_id,
null
)
) as deliver_order_cnt, -- 配送成交单量
-- 退款单量: 如果说退款ID存在, 说明订单一定是退款单
count( distinct
if(
refund_id is not null,
order_id,
null
)
) as refund_order_cnt,
count( distinct
if(
order_from = 'miniapp',
order_id,
NULL
)
) as mini_app_order_cnt, -- 小程序订单量
count( distinct
if(
order_from = 'android',
order_id,
NULL
)
) as android_order_cnt, -- Android订单量
count( distinct
if(
order_from = 'ios',
order_id,
NULL
)
) as ios_order_cnt, -- IOS订单量
count( distinct
if(
order_from = 'pcweb',
order_id,
NULL
)
) as pcweb_order_cnt, -- pc商城订单量
dt
from t1 where rn1 = 1
group by dt,brand_id,brand_name;
13.4Hive其他优化点
13.4.1.关联优化器:如果多个mr之间的操作的数据都是一样的,同样shuffle操作也是一样的,此时可以共享shuffle
一个SQL语句最终翻译为MR, 请问, 有没有可能出现翻译为多个MR的情况呢? 非常有可能的
每一个MR的中间都有可能会执行shuffle的操作, 而 shuffle其实比较消耗资源操作(内部存在多次IO操作)
开启关联优化器后, 如果多个MR中shuffle是一致的, 此时可以共享shuffle, 减少shuffle次数, 从而提升效率
配置项:
set hive.optimize.correlation=true;
13.4.2.Hive并行执行
2.1 并行编译:
hive.driver.parallel.compilation 是否开启并行编译
hive.driver.parallel.compilation.global.limit 设置最大同时编译多少个会话的SQL
如果设置为0/负值, 表示无限制
注意: 以上两个参数, 建议是直接在CM中开启, 并配置好即可
说明:
默认情况下, 如果HIVE有多个会话窗口, 而且多个窗口都在提交SQL , 此时HIVE默认只能对一个会话SQL进行编译, 其他会话的SQL需要等待, 这样的效率比较低
2.2 并行执行:
一个SQL语句在提交到HIVE之后, SQL有可能会被翻译为多个阶段, 在这个过程中, 有可能出现多个阶段互补干扰的情况, 这个时候, 可以安排多个阶段并行执行操作, 以此来提升效率
如何设置呢? 建议是在 会话中配置(压根无法在CM中配置)
set hive.exec.parallel=true: 可以开启并发执行,默认为false。
set hive.exec.parallel.thread.number=16; 同一个sql允许的最大并行度,默认为8。
注意:
开启了此配置后, 并不代表所有SQL一定会并行的执行, 因为是否可以并行执行, 还取决于SQL中多个阶段之间是否有依赖, 只有在没有依赖的时候, 才可能并行执行(还有前提, 资源得够)
13.4.3.HIVE中小文件合并操作
HIVE: 在执行完成后, 输出的结果的文件尽量的少一些, 避免出现一些小文件过多问题, 此时可以通过设置让SQL在执行的时候输出的文件尽可能少
hive.merge.mapfiles : 是否开启map端的小文件合并的操作 (指的MR程序只有map 没有reduce的操作)
hive.merge.mapredfiles : 是否开启reduce端的小文件合并的操作
hive.merge.size.per.task : 设置文件的大小(合并后的文件的最大值,默认为: 256M)
hive.merge.smallfiles.avgsize : 当输出文件的平均大小小于此设置值时候, 启动一个独立的MR任务进行文件合并操作, 默认值为 16M
说明: 以上参数配置, 直接在CM上进行配置即可
例如:
比如一个MR输出 10个文件
1M 10M 50M 2M 3M 30M 10M 5M 6M 20M
请问, 是否会进行文件合并操作吗?
取决于hive.merge.smallfiles.avgsize值, 默认为16M, 意味输出的多个文件平均大小小于等于16M的时候, 就会触发合并
计算后, 平均大小为 13.7M 那么就会触发合并操作
思考: 是将所有的文件合并为一个文件吗? 还是怎么处理呢?
取决于 hive.merge.size.per.task 参数设置, 如果这个参数设置为 128M, 意味着合并文件最大为128M
128M , 9M
13.4.4.矢量化查询(批量化)
配置: 建议会话中常开
set hive.vectorized.execution.enabled=true;
set hive.vectorized.execution.reduce.enabled = true;
说明:
一旦开启了矢量化查询的操作后, HIVE执行引擎在读取数据的时候, 就会采用批量化读取工作, 一次性读取 多条数据进行统一的处理操作, 从而减少读取的次数(减少磁盘IO次数), 从而提升效率
注意:
要求表的存储格式必须为ORC
13.4.5.读取零拷贝
在读取数据时候, 能少读一些, 尽量少读一些(没用的数据尽量不读)
配置: 建议在会话中常开
set hive.exec.orc.zerocopy=true;
注意: 表的存储格式必须为ORC
例如:
假设有一个A表, 表中 a b c三个字段
select a,b from A where a ='xxx'; c字段在整个SQL中都没有使用, 在读取数据时候, 我们就不读取c列数据
15.Presto相关内容
15.1Presto基本介绍
Presto是一个大数据旗下的分布式SQL查询引擎, Presto可以独立提供计算分析操作, 不需要依赖于其他的计算引擎, 而HIVE仅仅是一个工具, 最终计算是依赖于MR或者其他的执行引擎
Presto可以对接多种数据源, 可以从不同的数据源中读取数据进行分析处理, 一条presto查询可以将多个数据源进行合并, 可以跨越多个不同的组织进行分析
Presto是完全基于内存的计算引擎, 这也导致Presto不能对海量大量的数据进行统计分析操作, 数据集一般 在 GB ~ PB左右(集群数量越多, 资源越多, 可以计算的数据量越高)
性能对比图表:
应用场景:
1) 适用于对数据源统一查询 (多数据源场景)
2) 适用于 GB TB 快速的数据查询操作, 数据量越大, 资源占用越高
不适用场景:
1) 多张数据量比较大的表Join操作
2) 不适合进行清洗 转换 维度退化的相关操作, 主要应用在数据分析上
15.2.使用DBeaver连接Presto
15.3Presto的架构
整个presto是一个 M-S架构 (主从架构):
coordinator: 主节点
作用: 负责接收客户端发送的SQL, 对SQL进行编译, 形成执行计划, 根据执行计划, 分发给各个从节点进行执行操作
discovery service: 附属节点
作用: 一般内嵌在主节点中, 主要负责维护从节点列表, 当从节点启动后, 都需要到 discovery 节点进行注册操作
worker节点: 从节点
作用: 负责接收coordinator传递过来任务, 对任务进行具体处理工作(读取数据, 处理数据, 将处理后结果数据返回给coordinator)
Presto的相关时间函数
在presto中, 对于数据类型要求比较严格, 比如 数据字段类型为date类型,那么在基于这个字段进行过滤的时候, 编写的过滤条件上值必须也是date类型
日期转换操作:
date_format(timestamp,format): 将一个带有年月日 时分秒的日期对象 转换为字符串
date_parse(string,format) ---> timestamp: 将带有年月日 时分秒的日期字符串 转换为 日期对象
date(日期对象)--->date : 将带有年月日 时分秒的日期对象, 转换为仅包含年月日的日期对象
说明:
date类型: 表示只有年 月 日
timestamp类型: 表示 年 月 日 时 分 秒
format格式:
年:%Y
月:%m
日:%d
时:%H
分:%i
秒:%s
周几:%w(0..6)
-- timestamp '日期字符串数据' 直接转换为 日期对象 , 要求 后面的日期字符串的数据必须是标准格式的日期
-- 即使 日期字符串数据, 只有年月日 依然是可以转换的, 只不过 时分秒都是 0
select date_format( timestamp '2020-10-10 12:50:50' , '%Y/%m/%d %H:%i:%s' );
-- date_parse: 可以将指定日期格式数据转换为日期对象
select date_format( date_parse('2020-10-10 12:50:50','%Y-%m-%d %H:%i:%s') , '%Y/%m/%d %H:%i:%s' );
-- date '字符串' 直接转换为日期对象, 仅能处理标准的年月日的数据, 无法处理带有时分秒的操作
select date_format( date '2020-10-10' , '%Y/%m/%d %H:%i:%s' );
日期计算的操作:
date_add(unit,value,timestamp) → [same as input] : 根据 unit设置时间单位, 对timestamp的时间数据 进行 value计算 , 计算为 + 号 , 如果想计算减号, 可以让value为负数即可
date_diff(unit, timestamp1, timestamp2) → bigint : 根据 unit单位,时间timestamp2 - 时间timestamp1 , 得出差值
select date_add('month',-3, timestamp '2021-05-05 12:20:21'); -- 2021-02-05 12:20:21
select date_diff('month', timestamp '2020-07-30 12:20:21', timestamp '2021-05-01 12:20:21') -- 9
15.4Presto的内存调整
1) 各个节点 JVM内存推荐大小: 当前节点剩余内存 80%
2) 对于 memory.heap-headroom-per-node 第三方库的内存配置 : 建议 jvm内存的 15%左右, 默认为30%
3) 在配置的时候, 不要正正好后, 建议预留一点点, 以免出现问题,建议预留 5%~10%
4) 用户内存和系统内存之间比较, 一般是以 8/2原则或者 7/3原则
经验说明:
数据量在35TB, presto节点数量大约在30台左右(每台节点: 128GB内存 + 8核CPU)
在presto中特殊的SQL优化点:
1) 在进行group by 操作的时候,如果分组字段比较多, 将分组字段中, 通过distinct去重后, 值比较多的字段放置在前面, 比较少往后放置
好处: 可以已开启就被分为更多的组, 让更多的worker参与计算操作
比如说:
select from 表 group by uid,sex;
说明: 一个表中 uid的值有很多的不同, 但是 sex一般只有二个值, 此时将UID放置在最前面, 这样好处是presto在进行任务分配的时候, 可以让更多的worker参与进行计算操作
2) 在进行JOIN操作的时候, 要将大表放置join前面. 小表放置在Join后面
3) 尽量使用正则替换掉SQL中like查询操作
15.5Presto高级语法
Grouping Sets、CUBE和ROLLUP
Grouping sets:
当需要对表中各个字段进行分组操作的时候, 并且最终需要将各个分组的结果汇总在一个表的时候, 此时可以通过grouping sets 来简写, 将各个分组操作, 统一的放置在grouping sets(…) 中就OK了
使用grouping sets 可以一次性实现多个字段进行分别分组操作, 简化代码, 减少数据扫描. 提升效率
select
store_id,
null as group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by store_id
union all
select
null as store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by group_id
union all
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by store_id,group_id ;
-- 以上代码, 同一个表扫描三次, 执行了三次分组 对效率不太好
-- 思考, 是否可以只需要读取一次, 就完成三次分组操作呢?
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by grouping sets(store_id , group_id , (store_id,group_id) );
● cube:
○ CUBE操作会生成提供column所有可能的grouping sets结果
○ 例如:
■ 书写 store_id,group_id , 组合分组结果: 空 , store_id,group_id, ( store_id,group_id)
■ 书写 store_id,group_id,goods_id. 组合分组结果: 空,store_id,group_id,goods_id, (store_id,group_id),(group_id,goods_id),(store_id,group_id,goods_id)
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by cube(store_id,group_id)
相当于:
select
store_id,
null as group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by store_id
union all
select
null as store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by group_id
union all
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by group_id,store_id
union all
select
null as store_id,
null as group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by ()
相当于:
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by grouping sets( (), store_id, group_id, (store_id,group_id) )
● rollup:
○ 先将rollup中所有的字段全部组合在一起, 进行分组, 然后从后往前逐一递减进行分组,直到为空
■ 如果设置: rollup(store_id,group_id) , 分组情况: (store_id,group_id), store_id, ()
■ 如果设置: rollup(store_id,group_id,goods_id), 分组情况: (store_id,group_id,goods_id), (store_id,group_id),store_id,()
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by rollup(store_id,group_id)
相当于:
select
store_id,
group_id,
sum(order_amount) as total_price
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by grouping sets((store_id,group_id),store_id,())
说明: 对于 cube 以及 rollup 虽然可以提供更多的分组的情况, 但是实际使用中, 一般不用, 建议使用 grouping sets 来自定义自己分组情况即可, 这样更加灵活, 更加好理解
15.6.Grouping
select
store_id,
group_id,
sum(order_amount) as total_price,
grouping(store_id) as rn1, -- 如果只写一个, 返回结果只能 0 和 1
grouping(group_id) as rn2,
grouping(store_id,group_id) as rn3 -- 如果写了两个, 返回结果, 0 1 2 3 例如: 返回十进制 2, 二进制为 10 , 表示 按照group_id分组结果
from hive.bj59_yp_dwb_jiale.dwb_order_detail
group by grouping sets(store_id,group_id,(store_id,group_id));
15.7.DWS层实现操作
15.7.1.销售主题的日统计宽表
可分析的主要指标有:销售收入、平台收入、配送成交额、小程序成交额、安卓APP成交额、苹果APP成交额、PC商城成交额、订单量、参评单量、差评单量、配送单量、退款单量、小程序订单量、安卓APP订单量、苹果APP订单量、PC商城订单量。
维度有:日期、城市、商圈、店铺、品牌、大类、中类、小类。
维度组合:
日期: 日
日期 + 城市
日期 + 城市 + 商圈
日期 + 城市 + 商圈 + 店铺
日期 + 品牌
日期 + 大类
日期 + 大类 + 中类
日期 + 大类 + 中类 + 小类
16 * 8 = 128 个需求指标结果
分析, 当前需求统计的这些维度 和 指标, 需要涉及到那些表, 以及涉及到那些字段呢?
维度字段:
日期: dwb_order_detail.dt
城市: dwb_shop_detail: city_id 和 city_name
商圈: dwb_shop_detail: trade_area_id 和 trade_area_name
店铺: dwb_shop_detail: id 和 store_name
品牌: dwb_goods_detail: brand_id 和 brand_name
大类: dwb_goods_detail: max_class_id 和 max_class_name
中类: dwb_goods_detail: mid_class_id 和 mid_class_name
小类: dwb_goods_detail: min_class_id 和 min_class_name
指标字段:
订单量相关指标: dwb_order_detail.order_id
订单销售收入(销售收入, 小程序, 安卓, 苹果, pc端):dwb_order_detail.order_amount
平台收入: dwb_order_detail.plat_fee
配送费: wb_order_detail.delivery_fee
涉及表:
订单明细宽表(当前主题的事实表): dwb_order_detail (事实表)
店铺明细宽表: dwb_shop_detail (维度表)
商品明细宽表: dwb_goods_detail (维度表)
关联条件:
订单表 和 店铺表:
订单明细宽表.store_id = 店铺明细宽表.id
订单表 和 商品表:
订单明细宽表.goods_id = 商品明细宽表.id
思考: 当前这个是三种数仓模型那一种呢? 星型模型
是否需要过滤一些操作呢?
1- 保证必须是支付状态: is_pay = 1
2- 保证订单状态: order_state 不能是 1(已下单, 没有付款) 和 7 (已取消)
首先, 先创建DWS层库 和 表(销售主题日统计宽表) (在HIVE中重新建表)
1.-- 创建库:
create database if not exists bj59_yp_dws_jiale;
-- 创建表(指标字段 + 维度字段 + 经验字段 ):
DROP TABLE IF EXISTS bj59_yp_dws_jiale.dws_sale_daycount;
CREATE TABLE bj59_yp_dws_jiale.dws_sale_daycount(
--dt STRING,
province_id string COMMENT '省份id',
province_name string COMMENT '省份名称',
city_id string COMMENT '城市id',
city_name string COMMENT '城市name',
trade_area_id string COMMENT '商圈id',
trade_area_name string COMMENT '商圈名称',
store_id string COMMENT '店铺的id',
store_name string COMMENT '店铺名称',
brand_id string COMMENT '品牌id',
brand_name string COMMENT '品牌名称',
max_class_id string COMMENT '商品大类id',
max_class_name string COMMENT '大类名称',
mid_class_id string COMMENT '中类id',
mid_class_name string COMMENT '中类名称',
min_class_id string COMMENT '小类id',
min_class_name string COMMENT '小类名称',
-- 用于标记数据结果是按照那个维度来统计的一个经验字段
group_type string COMMENT '分组类型:store,trade_area,city,brand,min_class,mid_class,max_class, all',
-- =======日统计=======
-- 销售收入
sale_amt DECIMAL(38,2) COMMENT '销售收入',
-- 平台收入
plat_amt DECIMAL(38,2) COMMENT '平台收入',
-- 配送成交额
deliver_sale_amt DECIMAL(38,2) COMMENT '配送成交额',
-- 小程序成交额
mini_app_sale_amt DECIMAL(38,2) COMMENT '小程序成交额',
-- 安卓APP成交额
android_sale_amt DECIMAL(38,2) COMMENT '安卓APP成交额',
-- 苹果APP成交额
ios_sale_amt DECIMAL(38,2) COMMENT '苹果APP成交额',
-- PC商城成交额
pcweb_sale_amt DECIMAL(38,2) COMMENT 'PC商城成交额',
-- 成交单量
order_cnt BIGINT COMMENT '成交单量',
-- 参评单量
eva_order_cnt BIGINT COMMENT '参评单量comment=>cmt',
-- 差评单量
bad_eva_order_cnt BIGINT COMMENT '差评单量negtive-comment=>ncmt',
-- 配送成交单量
deliver_order_cnt BIGINT COMMENT '配送单量',
-- 退款单量
refund_order_cnt BIGINT COMMENT '退款单量',
-- 小程序成交单量
miniapp_order_cnt BIGINT COMMENT '小程序成交单量',
-- 安卓APP订单量
android_order_cnt BIGINT COMMENT '安卓APP订单量',
-- 苹果APP订单量
ios_order_cnt BIGINT COMMENT '苹果APP订单量',
-- PC商城成交单量
pcweb_order_cnt BIGINT COMMENT 'PC商城成交单量'
)
COMMENT '销售主题日统计宽表'
PARTITIONED BY(dt STRING)
ROW format delimited fields terminated BY '\t'
stored AS orc tblproperties ('orc.compress' = 'SNAPPY');
-- 第一步: 对数据进行去重操作, 通过row_number实现
insert into hive.bj59_yp_dws_jiale.dws_sale_daycount
with t1 as (
select
-- 维度字段:
o.dt, -- 日期维度
s.province_id ,
s.province_name ,
s.city_id ,
s.city_name , -- 城市维度
s.trade_area_id ,
s.trade_area_name , -- 商圈维度
o.store_id ,
s.store_name , -- 店铺维度
g.brand_id ,
g.brand_name, -- 品牌维度
g.max_class_id ,
g.max_class_name , -- 大类
g.mid_class_id ,
g.mid_class_name , -- 中类
g.min_class_id ,
g.min_class_name , -- 小类
-- 指标字段
o.order_id , -- 订单id 计算订单量相关指标
o.order_amount, -- 订单销售收入
o.total_price , -- 商品销售收入
o.plat_fee , -- 平台分润
o.delivery_fee , -- 配送费用
-- 用于判断的字段
o.order_from , -- 渠道(小程序, 安卓, 苹果, pc)
o.evaluation_id , -- 评价表 id , 判断是否有参评
o.delievery_id , -- 配送表id, 判断是否有配送
o.geval_scores , -- 综合评分: 10分制
o.refund_id , -- 退款表id,判断是否有退款
-- 进行去重操作:
-- 计算 日期, 日期+城市, 日期+城市+商圈 , 日期+城市+商圈+店铺
row_number() over(partition by o.order_id) as order_rn,
-- 计算 日期 + 品牌 : 第一个去重计算 订单量 第二个去重 计算 销售额
row_number() over(partition by o.order_id,g.brand_id) as brand_rn,
row_number() over(partition by o.order_id,o.goods_id ,g.brand_id) as brand_goods_rn,
-- 计算 日期 + 大类
row_number() over(partition by o.order_id,g.max_class_id) as max_class_rn,
row_number() over(partition by o.order_id,o.goods_id ,g.max_class_id) as max_class_goods_rn,
-- 计算 日期 + 大类 + 中类
row_number() over(partition by o.order_id,g.max_class_id,g.mid_class_id) as mid_class_rn,
row_number() over(partition by o.order_id,o.goods_id ,g.max_class_id,g.mid_class_id) as mid_class_goods_rn,
-- 计算 日期 + 大类 + 中类 + 小类
row_number() over(partition by o.order_id,g.max_class_id,g.mid_class_id,g.min_class_id) as min_class_rn,
row_number() over(partition by o.order_id,o.goods_id ,g.max_class_id,g.mid_class_id,g.min_class_id) as min_class_goods_rn
from hive.bj59_yp_dwb_jiale.dwb_order_detail o
left join hive.bj59_yp_dwb_jiale.dwb_shop_detail s on o.store_id = s.id
left join hive.bj59_yp_dwb_jiale.dwb_goods_detail g on o.goods_id = g.id
where o.is_pay = 1 and o.order_state not in(1,7)
)
select
-- 维度字段
province_id,
province_name ,
city_id ,
city_name ,
trade_area_id ,
trade_area_name ,
store_id ,
store_name ,
brand_id ,
brand_name ,
max_class_id ,
max_class_name,
mid_class_id ,
mid_class_name,
min_class_id ,
min_class_name,
-- group_type字段的值, 需要根据不同的维度分组, 标上不同的值
case
when grouping(store_id) = 0 then 'store'
when grouping(trade_area_id) = 0 then 'trade_area'
when grouping(city_id) = 0 then 'city'
when grouping(brand_id) = 0 then 'brand'
when grouping(min_class_id) = 0 then 'min_class'
when grouping(mid_class_id) = 0 then 'mid_class'
when grouping(max_class_id) = 0 then 'max_class'
when grouping(dt) = 0 then 'all'
else 'other'
end as group_type,
-- 销售收入:
case
when grouping(store_id) = 0 then sum( if(order_rn = 1 and store_id is not null,coalesce(order_amount,0),0) )
when grouping(trade_area_id) = 0 then sum( if(order_rn = 1 and trade_area_id is not null,coalesce(order_amount,0),0) )
when grouping(city_id) = 0 then sum( if(order_rn = 1 and city_id is not null,coalesce(order_amount,0),0) )
when grouping(brand_id) = 0 then sum( if( brand_goods_rn = 1 and brand_id is not null,coalesce(total_price,0),0) )
when grouping(min_class_id) = 0 then sum( if( min_class_goods_rn = 1 and min_class_id is not null,coalesce(total_price,0),0) )
when grouping(mid_class_id) = 0 then sum( if( mid_class_goods_rn = 1 and mid_class_id is not null,coalesce(total_price,0),0) )
when grouping(max_class_id) = 0 then sum( if( max_class_goods_rn = 1 and max_class_id is not null,coalesce(total_price,0),0) )
when grouping(dt) = 0 then sum( if(order_rn = 1 and dt is not null,coalesce(order_amount,0),0) )
else NULL
end as sale_amt,
-- 平台收入:
case
when grouping(store_id) = 0 then sum( if(order_rn = 1 and store_id is not null,coalesce(plat_fee,0),0) )
when grouping(trade_area_id) = 0 then sum( if(order_rn = 1 and trade_area_id is not null,coalesce(plat_fee,0),0) )
when grouping(city_id) = 0 then sum( if(order_rn = 1 and city_id is not null,coalesce(plat_fee,0),0) )
when grouping(brand_id) = 0 then null
when grouping(min_class_id) = 0 then null
when grouping(mid_class_id) = 0 then null
when grouping(max_class_id) = 0 then null
when grouping(dt) = 0 then sum( if(order_rn = 1 and dt is not null,coalesce(plat_fee,0),0) )
else NULL
end as plat_amt,
-- 配送成交额
case
when grouping(store_id) = 0 then sum( if(order_rn = 1 and store_id is not null,coalesce(delivery_fee,0),0) )
when grouping(trade_area_id) = 0 then sum( if(order_rn = 1 and trade_area_id is not null,coalesce(delivery_fee,0),0) )
when grouping(city_id) = 0 then sum( if(order_rn = 1 and city_id is not null,coalesce(delivery_fee,0),0) )
when grouping(brand_id) = 0 then null
when grouping(min_class_id) = 0 then null
when grouping(mid_class_id) = 0 then null
when grouping(max_class_id) = 0 then null
when grouping(dt) = 0 then sum( if(order_rn = 1 and dt is not null,coalesce(delivery_fee,0),0) )
else NULL
end as deliver_sale_amt,
-- 小程序成交额:
case
when grouping(store_id) = 0
then sum(
if(
order_rn = 1 and store_id is not null and order_from = 'miniapp',
coalesce(order_amount,0),
0
)
)
when grouping(trade_area_id) = 0
then sum(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'miniapp',
coalesce(order_amount,0),
0
)
)
when grouping(city_id) = 0
then sum(
if(
order_rn = 1 and city_id is not null and order_from = 'miniapp',
coalesce(order_amount,0),
0
)
)
when grouping(brand_id) = 0
then sum(
if(
brand_goods_rn = 1 and brand_id is not null and order_from = 'miniapp',
coalesce(total_price,0),
0
)
)
when grouping(min_class_id) = 0
then sum(
if(
min_class_goods_rn = 1 and min_class_id is not null and order_from = 'miniapp',
coalesce(total_price,0),
0
)
)
when grouping(mid_class_id) = 0
then sum(
if(
mid_class_goods_rn = 1 and mid_class_id is not null and order_from = 'miniapp',
coalesce(total_price,0),
0
)
)
when grouping(max_class_id) = 0
then sum(
if(
max_class_goods_rn = 1 and max_class_id is not null and order_from = 'miniapp',
coalesce(total_price,0),
0
)
)
when grouping(dt) = 0
then sum(
if(
order_rn = 1 and dt is not null and order_from = 'miniapp',
coalesce(order_amount,0),
0
)
)
else NULL
end as mini_app_sale_amt,
-- 安卓成交额
case
when grouping(store_id) = 0
then sum(
if(
order_rn = 1 and store_id is not null and order_from = 'android',
coalesce(order_amount,0),
0
)
)
when grouping(trade_area_id) = 0
then sum(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'android',
coalesce(order_amount,0),
0
)
)
when grouping(city_id) = 0
then sum(
if(
order_rn = 1 and city_id is not null and order_from = 'android',
coalesce(order_amount,0),
0
)
)
when grouping(brand_id) = 0
then sum(
if(
brand_goods_rn = 1 and brand_id is not null and order_from = 'android',
coalesce(total_price,0),
0
)
)
when grouping(min_class_id) = 0
then sum(
if(
min_class_goods_rn = 1 and min_class_id is not null and order_from = 'android',
coalesce(total_price,0),
0
)
)
when grouping(mid_class_id) = 0
then sum(
if(
mid_class_goods_rn = 1 and mid_class_id is not null and order_from = 'android',
coalesce(total_price,0),
0
)
)
when grouping(max_class_id) = 0
then sum(
if(
max_class_goods_rn = 1 and max_class_id is not null and order_from = 'android',
coalesce(total_price,0),
0
)
)
when grouping(dt) = 0
then sum(
if(
order_rn = 1 and dt is not null and order_from = 'android',
coalesce(order_amount,0),
0
)
)
else NULL
end as android_sale_amt,
-- 苹果成交额
case
when grouping(store_id) = 0
then sum(
if(
order_rn = 1 and store_id is not null and order_from = 'ios',
coalesce(order_amount,0),
0
)
)
when grouping(trade_area_id) = 0
then sum(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'ios',
coalesce(order_amount,0),
0
)
)
when grouping(city_id) = 0
then sum(
if(
order_rn = 1 and city_id is not null and order_from = 'ios',
coalesce(order_amount,0),
0
)
)
when grouping(brand_id) = 0
then sum(
if(
brand_goods_rn = 1 and brand_id is not null and order_from = 'ios',
coalesce(total_price,0),
0
)
)
when grouping(min_class_id) = 0
then sum(
if(
min_class_goods_rn = 1 and min_class_id is not null and order_from = 'ios',
coalesce(total_price,0),
0
)
)
when grouping(mid_class_id) = 0
then sum(
if(
mid_class_goods_rn = 1 and mid_class_id is not null and order_from = 'ios',
coalesce(total_price,0),
0
)
)
when grouping(max_class_id) = 0
then sum(
if(
max_class_goods_rn = 1 and max_class_id is not null and order_from = 'ios',
coalesce(total_price,0),
0
)
)
when grouping(dt) = 0
then sum(
if(
order_rn = 1 and dt is not null and order_from = 'ios',
coalesce(order_amount,0),
0
)
)
else NULL
end as ios_sale_amt,
-- PC成交额
case
when grouping(store_id) = 0
then sum(
if(
order_rn = 1 and store_id is not null and order_from = 'pcweb',
coalesce(order_amount,0),
0
)
)
when grouping(trade_area_id) = 0
then sum(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'pcweb',
coalesce(order_amount,0),
0
)
)
when grouping(city_id) = 0
then sum(
if(
order_rn = 1 and city_id is not null and order_from = 'pcweb',
coalesce(order_amount,0),
0
)
)
when grouping(brand_id) = 0
then sum(
if(
brand_goods_rn = 1 and brand_id is not null and order_from = 'pcweb',
coalesce(total_price,0),
0
)
)
when grouping(min_class_id) = 0
then sum(
if(
min_class_goods_rn = 1 and min_class_id is not null and order_from = 'pcweb',
coalesce(total_price,0),
0
)
)
when grouping(mid_class_id) = 0
then sum(
if(
mid_class_goods_rn = 1 and mid_class_id is not null and order_from = 'pcweb',
coalesce(total_price,0),
0
)
)
when grouping(max_class_id) = 0
then sum(
if(
max_class_goods_rn = 1 and max_class_id is not null and order_from = 'pcweb',
coalesce(total_price,0),
0
)
)
when grouping(dt) = 0
then sum(
if(
order_rn = 1 and dt is not null and order_from = 'pcweb',
coalesce(order_amount,0),
0
)
)
else NULL
end as pcweb_sale_amt,
--- 订单量相关指标:
-- 成交单量:
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null,
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null,
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null,
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null,
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null,
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null,
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null,
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null,
order_id,
NULL
)
)
else NULL
end as order_cnt,
-- 参评单量:
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and evaluation_id is not null ,
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and evaluation_id is not null,
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and evaluation_id is not null,
order_id,
NULL
)
)
else NULL
end as eva_order_cnt,
-- 差评单量:
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and evaluation_id is not null and geval_scores < 6,
order_id,
NULL
)
)
else NULL
end as bad_eva_order_cnt,
-- 配送成交单量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and delievery_id is not null,
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and delievery_id is not null,
order_id,
NULL
)
)
else NULL
end as deliver_order_cnt,
-- 退款成交单量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and refund_id is not null,
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and refund_id is not null,
order_id,
NULL
)
)
else NULL
end as refund_order_cnt,
-- 小程序成交量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and order_from = 'miniapp',
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and order_from = 'miniapp',
order_id,
NULL
)
)
else NULL
end as miniapp_order_cnt,
-- 安卓成交量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and order_from = 'android',
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and order_from = 'android',
order_id,
NULL
)
)
else NULL
end as android_order_cnt,
-- 苹果成交量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and order_from = 'ios',
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and order_from = 'ios',
order_id,
NULL
)
)
else NULL
end as ios_order_cnt,
-- pc成交量
case
when grouping(store_id) = 0
then count(
if(
order_rn = 1 and store_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(trade_area_id) = 0
then count(
if(
order_rn = 1 and trade_area_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(city_id) = 0
then count(
if(
order_rn = 1 and city_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(brand_id) = 0
then count(
if(
brand_rn = 1 and brand_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(min_class_id) = 0
then count(
if(
min_class_rn = 1 and min_class_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(mid_class_id) = 0
then count(
if(
mid_class_rn = 1 and mid_class_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(max_class_id) = 0
then count(
if(
max_class_rn = 1 and max_class_id is not null and order_from = 'pcweb',
order_id,
NULL
)
)
when grouping(dt) = 0
then count(
if(
order_rn = 1 and dt is not null and order_from = 'pcweb',
order_id,
NULL
)
)
else NULL
end as pcweb_order_cnt,
dt
from t1
group by grouping sets(
dt,
(dt,province_id,province_name,city_id,city_name),
(dt,province_id,province_name,city_id,city_name,trade_area_id,trade_area_name),
(dt,province_id,province_name,city_id,city_name,trade_area_id,trade_area_name,store_id,store_name),
(dt,brand_id,brand_name),
(dt,max_class_id,max_class_name),
(dt,max_class_id,max_class_name,mid_class_id,mid_class_name),
(dt,max_class_id,max_class_name,mid_class_id,mid_class_name,min_class_id,min_class_name)
)
15.2.按月统计:
insert into xxxxx_yp_dm_jiale.dm_sale
with dim_date as (
select
date_code,
year_code,
year_month,
month_code ,
day_month_num ,
dim_date_id ,
year_week_name_cn
from xxxxx_yp_dwd_jiale.dim_date
),
t1 as (
select
'2022-05-10' as date_time,
'month' as time_type,
d.year_code ,
d.year_month,
d.month_code,
null as day_month_num ,
null as dim_date_id,
null as year_week_name_cn ,
-- 处理维度:
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then 'store'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then 'trade_area'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then 'city'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then 'brand'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then 'min_class'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then 'mid_class'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then 'max_class'
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then 'all'
else null
end as group_type,
s.city_id ,
s.city_name ,
s.trade_area_id ,
s.trade_area_name,
s.store_id,
s.store_name ,
s.brand_id,
s.brand_name,
s.max_class_id,
s.max_class_name,
s.mid_class_id,
s.mid_class_name,
s.min_class_id,
s.min_class_name,
-- 指标:
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.sale_amt)
else null
end as sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.plat_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.plat_amt)
else null
end as plat_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.deliver_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.deliver_sale_amt)
else null
end as deliver_sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.mini_app_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.mini_app_sale_amt)
else null
end as mini_app_sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.android_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.android_sale_amt)
else null
end as android_sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.ios_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.ios_sale_amt)
else null
end as ios_sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.pcweb_sale_amt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.pcweb_sale_amt)
else null
end as pcweb_sale_amt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.order_cnt)
else null
end as order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.eva_order_cnt)
else null
end as eva_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.bad_eva_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.bad_eva_order_cnt)
else null
end as bad_eva_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.deliver_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.deliver_order_cnt)
else null
end as deliver_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.refund_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.refund_order_cnt)
else null
end as refund_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.miniapp_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.miniapp_order_cnt)
else null
end as miniapp_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.android_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.android_order_cnt)
else null
end as android_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.ios_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.ios_order_cnt)
else null
end as ios_order_cnt,
case
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 15 and s.group_type = 'store'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 31 and s.group_type = 'trade_area'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 63 and s.group_type = 'city'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 119 and s.group_type = 'brand'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 120 and s.group_type = 'min_class'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 121 and s.group_type = 'mid_class'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 123 and s.group_type = 'max_class'
then sum(s.pcweb_order_cnt)
when grouping(d.year_month,s.city_id ,s.trade_area_id ,s.store_id,s.brand_id ,s.max_class_id ,s.mid_class_id ,s.min_class_id ) = 127 and s.group_type = 'all'
then sum(s.pcweb_order_cnt)
else null
end as pcweb_order_cnt
from xxxxx.dws_sale_daycount s left join dim_date d on s.dt = d.date_code
group by
grouping sets(
-- 日期 + group_type
(d.year_code,d.year_month,d.month_code,s.group_type) ,
-- 日期 + 城市 + group_type
(d.year_code,d.year_month,d.month_code,s.city_id ,s.city_name ,s.group_type),
-- 日期 + 城市 + 商圈 + group_type
(d.year_code,d.year_month,d.month_code,s.city_id ,s.city_name,s.trade_area_id ,s.trade_area_name ,s.group_type),
-- 日期 + 城市 + 商圈 + 店铺 + group_type
(d.year_code,d.year_month,d.month_code,s.city_id ,s.city_name,s.trade_area_id ,s.trade_area_name ,s.store_id,s.store_name ,s.group_type),
-- 日期 + 品牌 + group_type
(d.year_code,d.year_month,d.month_code,s.brand_id ,s.brand_name ,s.group_type) ,
-- 日期 + 大类 + group_type
(d.year_code,d.year_month,d.month_code,s.max_class_id ,s.max_class_name ,s.group_type) ,
-- 日期 + 大类 + 中类 + group_type
(d.year_code,d.year_month,d.month_code,s.max_class_id ,s.max_class_name ,s.mid_class_id ,s.mid_class_name, s.group_type),
-- 日期 + 大类 + 中类 + 小类 + group_type
(d.year_code,d.year_month,d.month_code,s.max_class_id ,s.max_class_name ,s.mid_class_id ,s.mid_class_name,s.min_class_id ,s.min_class_name ,s.group_type)
)
)
select * from t1 where group_type is not null;
15.7.2商品主题统计宽表
主要指标有:下单次数、下单件数、下单金额、被支付次数、被支付件数、被支付金额、被退款次数、被退款件数、被退款金额、被加入购物车次数、被加入购物车件数、被收藏次数、好评数、中评数、差评数。
维度有:商品、日期。
● 创建商品主题统计宽表(HIVE执行)
create table bj59_yp_dws_jiale.dws_sku_daycount (
dt STRING,
sku_id string comment 'sku_id',
sku_name string comment '商品名称',
order_count bigint comment '被下单次数',
order_num bigint comment '被下单件数',
order_amount decimal(38,2) comment '被下单金额',
payment_count bigint comment '被支付次数',
payment_num bigint comment '被支付件数',
payment_amount decimal(38,2) comment '被支付金额',
refund_count bigint comment '被退款次数',
refund_num bigint comment '被退款件数',
refund_amount decimal(38,2) comment '被退款金额',
cart_count bigint comment '被加入购物车次数',
cart_num bigint comment '被加入购物车件数',
favor_count bigint comment '被收藏次数',
evaluation_good_count bigint comment '好评数',
evaluation_mid_count bigint comment '中评数',
evaluation_bad_count bigint comment '差评数'
) COMMENT '每日商品行为'
--PARTITIONED BY(dt STRING)
ROW format delimited fields terminated BY '\t'
stored AS orc tblproperties ('orc.compress' = 'SNAPPY');
需求分析
主要指标有:
下单次数、
下单件数、
下单金额、
被支付次数、
被支付件数、
被支付金额、
被退款次数、
被退款件数、
被退款金额、
被加入购物车次数、
被加入购物车件数、
被收藏次数、
好评数、
中评数、
差评数。
维度有:
商品 + 日期。
下单次数、 下单件数、 下单金额:
表: 订单明细宽表 dwb_order_detail
涉及字段:
维度字段:
商品维度: goods_id, goods_name
日期: dt
指标字段:
次数: 产生了多少个订单 order_id
件数: buy_num
金额: total_price
被支付次数、被支付件数、被支付金额:
表: 订单明细宽表 dwb_order_detail
过滤出已经支付的订单数据: is_pay = 1 and order_state not in(1,7)
涉及字段:
维度字段:
商品维度: goods_id, goods_name
日期: dt
指标字段:
次数: 产生了多少个订单 order_id
件数: buy_num
金额: total_price
被退款次数、被退款件数、被退款金额:
表: 订单明细宽表 dwb_order_detail
过滤出已经退款的订单数据: refund_id is not null and refund_state = 5 (退款完成)
涉及字段:
维度字段:
商品维度: goods_id, goods_name
日期: dt
指标字段:
次数: 产生了多少个订单 order_id
件数: buy_num
金额: total_price
被加入购物车次数、被加入购物车件数:
表: dwd.fact_shop_cart(购物车表) 在此表放置的数据, 都是购物车的相关数据 和 dwb_goods_detail(商品明细宽表)
关联条件:
c.goods_id = g.goods_id and c.end_date = '9999-99-99'
涉及字段:
维度字段:
商品维度: c.goods_id 和 g.goods_name
时间维度: c.create_time (此处不要使用 start_date, 因为这个起始时间, 有可能是采集数据时间, 并不是数据创建时间)
指标字段:
次数: c.id
件数: c.buy_num
被收藏次数:
表: dwd.fact_goods_collect(收藏表) 和 dwb_goods_detail(商品明细宽表)
关联条件:
c.goods_id = g.goods_id and c.end_date = '9999-99-99'
涉及字段:
维度字段:
商品维度: c.goods_id , g.goods_name
时间维度: c.create_time
维度字段:
次数: c.id
好评数、中评数、差评数:
表: dwd.fact_goods_evaluation_detail(商品评价表) 和 dwb_goods_detail(商品明细宽表)
关联条件:
eva.goods_id = g.goods_id and eva.end_date = '9999-99-99'
判断好评 中评 差评呢?
eva.geval_scores_goods 10分制
好评: 大于 8分, 中评 6~8 差评 6以下
涉及字段:
涉及维度:
商品维度: eva.goods_id 和 g.goods_name
时间维度: eva.create_time
指标字段:
数量: eva.id
sql实现;目前将每一个指定的统计结果,都放置在一个临时表中
-- 对订单表进行指标计算的时候, 需要先去重(由于我们是基于商品统计, 需要保证每笔订单中有不同的商品, 相同商品去除掉)
with t1 as (
select
-- 维度字段:
dt, -- 订单生成时间
pay_time, --支付时间
apply_date, -- 退款时间
goods_id,
goods_name,
-- 指标字段
order_id, -- 计算次数
buy_num , -- 商品数量, 计算件数
total_price , -- 商品金额, 计算金额
-- 判断字段:
is_pay , -- 是否支付, 为1表示支付
order_state , -- 订单状态, 不能为 1和7
refund_id , -- 退款id 不能为null
refund_state , -- 退款状态 必须为 5
-- 去重操作
row_number() over(partition by order_id,goods_id) as rn1
from hive.bj59_yp_dwb_jiale.dwb_order_detail
),
-- 下单次数、 下单件数、 下单金额:
order_goods_1 as (
select
dt,
goods_id,
goods_name,
count(order_id) as order_count,
sum(buy_num) as order_num,
sum(total_price) as order_amount
from t1 where rn1 =1
group by goods_id,goods_name,dt
),
-- 被支付次数、被支付件数、被支付金额:
payment_2 AS (
select
substr(pay_time,1,10) as dt,
goods_id,
goods_name,
count(order_id) as payment_count,
sum(buy_num) as payment_num,
sum(total_price) as payment_amount
from t1 where rn1 =1 and is_pay = 1 and order_state not in(1,7)
group by substr(pay_time,1,10),goods_id,goods_name
),
-- 被退款的次数, 被退款的件数, 被退款的金额
refund_3 AS (
SELECT
substr(apply_date,1,10) AS dt ,
goods_id,
goods_name,
count(order_id) as refund_count,
sum(buy_num) as refund_num,
sum(total_price) as refund_amount
FROM t1 WHERE rn1 = 1 AND refund_id NOT NULL AND refund_state = 5
GROUP BY substr(apply_date,1,10), goods_id,goods_name
),
-- 被收藏次数
favor_4 as (
SELECT
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) AS favor_count
FROM hive.xxxx.fact_goods_collect c
JOIN hive.bj59_yp_dwb_jiale.dwb_goods_detail g
ON c.goods_id = g.goods_id and c.end_date = '9999-99-99'
GROUP BY substr(c.create_time,1,10),c.goods_id,g.goods_name
)
-- 加入购物车的次数和 件数
cart_5 as (
select
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) as cart_count,
sum(c.buy_num) as cart_num
from hive.xxxx.fact_shop_cart c
JOIN hive.bj59_yp_dwb_jiale.dwb_goods_detail g
ON c.goods_id = g.goods_id and c.end_date = '9999-99-99'
group by substr(c.create_time,1,10),c.goods_id,g.goods_name
)
eva_6 as (
select
substr(eva.create_time,1,10) as dt,
eva.goods_id,
g.goods_name,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods > 8,
eva.id,
null
)
) as evaluation_good_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods between 6 and 8,
eva.id,
null
)
) as evaluation_mid_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods < 6,
eva.id,
null
)
) as evaluation_bad_count
from hive.xxxx.fact_goods_evaluation_detail eva
join hive.bj59_yp_dwb_jiale.dwb_goods_detail g
on eva.goods_id = g.goods_id and eva.end_date = '9999-99-99'
group by substr(eva.create_time,1,10),eva.goods_id,g.goods_name
)
第二步: 将结果灌入到目标表: 需要将多个临表全部合并在一起, 灌入到目标表
-- 对订单表进行指标计算的时候, 需要先去重(由于我们是基于商品统计, 需要保证每笔订单中有不同的商品, 相同商品去除掉)
insert into hive.bj59_yp_dws_jiale.dws_sku_daycount
with t1 as (
select
-- 维度字段:
dt, -- 订单生成时间
pay_time, --支付时间
apply_date, -- 退款时间
goods_id,
goods_name,
-- 指标字段
order_id, -- 计算次数
buy_num , -- 商品数量, 计算件数
total_price , -- 商品金额, 计算金额
-- 判断字段:
is_pay , -- 是否支付, 为1表示支付
order_state , -- 订单状态, 不能为 1和7
refund_id , -- 退款id 不能为null
refund_state , -- 退款状态 必须为 5
-- 去重操作
row_number() over(partition by order_id,goods_id) as rn1
from hive.xxxx.dwb_order_detail
),
-- 下单次数、 下单件数、 下单金额:
order_goods_1 as (
select
dt,
goods_id,
goods_name,
count(order_id) as order_count,
sum(buy_num) as order_num,
sum(total_price) as order_amount
from t1 where rn1 =1
group by goods_id,goods_name,dt
),
-- 被支付次数、被支付件数、被支付金额:
payment_2 AS (
select
substr(pay_time,1,10) as dt,
goods_id,
goods_name,
count(order_id) as payment_count,
sum(buy_num) as payment_num,
sum(total_price) as payment_amount
from t1 where rn1 =1 and is_pay = 1 and order_state not in(1,7)
group by substr(pay_time,1,10),goods_id,goods_name
),
-- 被退款的次数, 被退款的件数, 被退款的金额
refund_3 AS (
SELECT
substr(apply_date,1,10) AS dt ,
goods_id,
goods_name,
count(order_id) as refund_count,
sum(buy_num) as refund_num,
sum(total_price) as refund_amount
FROM t1 WHERE rn1 = 1 AND refund_id is NOT NULL AND refund_state = 5
GROUP BY substr(apply_date,1,10), goods_id,goods_name
),
-- 被收藏次数
favor_4 as (
SELECT
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) AS favor_count
FROM hive.bj59_yp_dwd_jiale.fact_goods_collect c
JOIN hive.xxxx.dwb_goods_detail g
ON c.goods_id = g.id and c.end_date = '9999-99-99'
GROUP BY substr(c.create_time,1,10),c.goods_id,g.goods_name
),
-- 加入购物车的次数和 件数
cart_5 as (
select
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) as cart_count,
sum(c.buy_num) as cart_num
from hive.bj59_yp_dwd_jiale.fact_shop_cart c
JOIN hive.xxxx.dwb_goods_detail g
ON c.goods_id = g.id and c.end_date = '9999-99-99'
group by substr(c.create_time,1,10),c.goods_id,g.goods_name
),
eva_6 as (
select
substr(eva.create_time,1,10) as dt,
eva.goods_id,
g.goods_name,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods > 8,
eva.id,
null
)
) as evaluation_good_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods between 6 and 8,
eva.id,
null
)
) as evaluation_mid_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods < 6,
eva.id,
null
)
) as evaluation_bad_count
from hive.bj59_yp_dwd_jiale.fact_goods_evaluation_detail eva
join hive.xxxx.dwb_goods_detail g
on eva.goods_id = g.id and eva.end_date = '9999-99-99'
group by substr(eva.create_time,1,10),eva.goods_id,g.goods_name
),
t7 as (
select
coalesce(t1.dt,t2.dt,t3.dt,t4.dt,t5.dt,t6.dt) as dt,
coalesce(t1.goods_id,t2.goods_id,t3.goods_id,t4.goods_id,t5.goods_id,t6.goods_id) as sku_id,
coalesce(t1.goods_name,t2.goods_name,t3.goods_name,t4.goods_name,t5.goods_name,t6.goods_name) as sku_name,
coalesce(t1.order_count,0) as order_count,
coalesce(t1.order_num,0) as order_num,
coalesce(t1.order_amount,0) as order_amount,
coalesce(t2.payment_count,0) as payment_count,
coalesce(t2.payment_num,0) as payment_num,
coalesce(t2.payment_amount,0) as payment_amount,
coalesce(t3.refund_count,0) as refund_count,
coalesce(t3.refund_num,0) as refund_num,
coalesce(t3.refund_amount,0) as refund_amount,
coalesce(t5.cart_count,0) as cart_count,
coalesce(t5.cart_num,0) as cart_num,
coalesce(t4.favor_count,0) as favor_count,
coalesce(t6.evaluation_good_count,0) as evaluation_good_count,
coalesce(t6.evaluation_mid_count,0) as evaluation_mid_count,
coalesce(t6.evaluation_bad_count,0) as evaluation_bad_count
from order_goods_1 t1
full join payment_2 t2 on t1.dt = t2.dt and t1.goods_id = t2.goods_id
full join refund_3 t3 on t3.dt = t2.dt and t3.goods_id = t2.goods_id
full join favor_4 t4 on t4.dt = t3.dt and t4.goods_id = t3.goods_id
full join cart_5 t5 on t5.dt = t4.dt and t5.goods_id = t4.goods_id
full join eva_6 t6 on t6.dt = t5.dt and t6.goods_id = t5.goods_id
)
select
dt,
sku_id,
sku_name,
sum(order_count) as order_count,
sum(order_num) as order_num,
sum(order_amount) as order_amount,
sum(payment_count) as payment_count,
sum(payment_num) as payment_num,
sum(payment_amount) as payment_amount,
sum(refund_count) as refund_count,
sum(refund_num) as refund_num,
sum(refund_amount) as refund_amount,
sum(cart_count) as cart_count,
sum(cart_num) as cart_num,
sum(favor_count) as favor_count,
sum(evaluation_good_count) as evaluation_good_count,
sum(evaluation_mid_count) as evaluation_mid_count,
sum(evaluation_bad_count) as evaluation_bad_count
from t7
group by dt,sku_id,sku_name;-- 对订单表进行指标计算的时候, 需要先去重(由于我们是基于商品统计, 需要保证每笔订单中有不同的商品, 相同商品去除掉)
insert into hive.bj59_yp_dws_jiale.dws_sku_daycount
with t1 as (
select
-- 维度字段:
dt, -- 订单生成时间
pay_time, --支付时间
apply_date, -- 退款时间
goods_id,
goods_name,
-- 指标字段
order_id, -- 计算次数
buy_num , -- 商品数量, 计算件数
total_price , -- 商品金额, 计算金额
-- 判断字段:
is_pay , -- 是否支付, 为1表示支付
order_state , -- 订单状态, 不能为 1和7
refund_id , -- 退款id 不能为null
refund_state , -- 退款状态 必须为 5
-- 去重操作
row_number() over(partition by order_id,goods_id) as rn1
from hive.xxxx.dwb_order_detail
),
-- 下单次数、 下单件数、 下单金额:
order_goods_1 as (
select
dt,
goods_id,
goods_name,
count(order_id) as order_count,
sum(buy_num) as order_num,
sum(total_price) as order_amount
from t1 where rn1 =1
group by goods_id,goods_name,dt
),
-- 被支付次数、被支付件数、被支付金额:
payment_2 AS (
select
substr(pay_time,1,10) as dt,
goods_id,
goods_name,
count(order_id) as payment_count,
sum(buy_num) as payment_num,
sum(total_price) as payment_amount
from t1 where rn1 =1 and is_pay = 1 and order_state not in(1,7)
group by substr(pay_time,1,10),goods_id,goods_name
),
-- 被退款的次数, 被退款的件数, 被退款的金额
refund_3 AS (
SELECT
substr(apply_date,1,10) AS dt ,
goods_id,
goods_name,
count(order_id) as refund_count,
sum(buy_num) as refund_num,
sum(total_price) as refund_amount
FROM t1 WHERE rn1 = 1 AND refund_id is NOT NULL AND refund_state = 5
GROUP BY substr(apply_date,1,10), goods_id,goods_name
),
-- 被收藏次数
favor_4 as (
SELECT
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) AS favor_count
FROM hive.bj59_yp_dwd_jiale.fact_goods_collect c
JOIN hive.xxxx.dwb_goods_detail g
ON c.goods_id = g.id and c.end_date = '9999-99-99'
GROUP BY substr(c.create_time,1,10),c.goods_id,g.goods_name
),
-- 加入购物车的次数和 件数
cart_5 as (
select
substr(c.create_time,1,10) as dt,
c.goods_id,
g.goods_name,
count(c.id) as cart_count,
sum(c.buy_num) as cart_num
from hive.bj59_yp_dwd_jiale.fact_shop_cart c
JOIN hive.xxxx.dwb_goods_detail g
ON c.goods_id = g.id and c.end_date = '9999-99-99'
group by substr(c.create_time,1,10),c.goods_id,g.goods_name
),
eva_6 as (
select
substr(eva.create_time,1,10) as dt,
eva.goods_id,
g.goods_name,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods > 8,
eva.id,
null
)
) as evaluation_good_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods between 6 and 8,
eva.id,
null
)
) as evaluation_mid_count,
count(
if(
eva.geval_scores_goods is not null and eva.geval_scores_goods < 6,
eva.id,
null
)
) as evaluation_bad_count
from hive.bj59_yp_dwd_jiale.fact_goods_evaluation_detail eva
join hive.xxxx.dwb_goods_detail g
on eva.goods_id = g.id and eva.end_date = '9999-99-99'
group by substr(eva.create_time,1,10),eva.goods_id,g.goods_name
),
t7 as (
select
coalesce(t1.dt,t2.dt,t3.dt,t4.dt,t5.dt,t6.dt) as dt,
coalesce(t1.goods_id,t2.goods_id,t3.goods_id,t4.goods_id,t5.goods_id,t6.goods_id) as sku_id,
coalesce(t1.goods_name,t2.goods_name,t3.goods_name,t4.goods_name,t5.goods_name,t6.goods_name) as sku_name,
coalesce(t1.order_count,0) as order_count,
coalesce(t1.order_num,0) as order_num,
coalesce(t1.order_amount,0) as order_amount,
coalesce(t2.payment_count,0) as payment_count,
coalesce(t2.payment_num,0) as payment_num,
coalesce(t2.payment_amount,0) as payment_amount,
coalesce(t3.refund_count,0) as refund_count,
coalesce(t3.refund_num,0) as refund_num,
coalesce(t3.refund_amount,0) as refund_amount,
coalesce(t5.cart_count,0) as cart_count,
coalesce(t5.cart_num,0) as cart_num,
coalesce(t4.favor_count,0) as favor_count,
coalesce(t6.evaluation_good_count,0) as evaluation_good_count,
coalesce(t6.evaluation_mid_count,0) as evaluation_mid_count,
coalesce(t6.evaluation_bad_count,0) as evaluation_bad_count
from order_goods_1 t1
full join payment_2 t2 on t1.dt = t2.dt and t1.goods_id = t2.goods_id
full join refund_3 t3 on t3.dt = t2.dt and t3.goods_id = t2.goods_id
full join favor_4 t4 on t4.dt = t3.dt and t4.goods_id = t3.goods_id
full join cart_5 t5 on t5.dt = t4.dt and t5.goods_id = t4.goods_id
full join eva_6 t6 on t6.dt = t5.dt and t6.goods_id = t5.goods_id
)
select
dt,
sku_id,
sku_name,
sum(order_count) as order_count,
sum(order_num) as order_num,
sum(order_amount) as order_amount,
sum(payment_count) as payment_count,
sum(payment_num) as payment_num,
sum(payment_amount) as payment_amount,
sum(refund_count) as refund_count,
sum(refund_num) as refund_num,
sum(refund_amount) as refund_amount,
sum(cart_count) as cart_count,
sum(cart_num) as cart_num,
sum(favor_count) as favor_count,
sum(evaluation_good_count) as evaluation_good_count,
sum(evaluation_mid_count) as evaluation_mid_count,
sum(evaluation_bad_count) as evaluation_bad_count
from t7
group by dt,sku_id,sku_name;
15.8 DM层实现操作
DM层的作用: 进行上卷细化统计操作
● 创建DM层的库(hive中执行)
create database IF NOT EXISTS xxx;
15.8.1 销售主题统计宽表
可分析的主要指标有:销售收入、平台收入、配送成交额、小程序成交额、安卓APP成交额、苹果APP成交额、PC商城成交额、订单量、参评单量、差评单量、配送单量、退款单量、小程序订单量、安卓APP订单量、苹果APP订单量、PC商城订单量。
维度有:日期(天(已经统计过), 周, 月, 年)、城市、商圈、店铺、品牌、大类、中类、小类
建表语句:
CREATE TABLE xxx.dm_sale(
date_time string COMMENT '统计日期,不能用来分组统计',
time_type string COMMENT '统计时间维度:year、month、week、date',
year_code string COMMENT '年code',
year_month string COMMENT '年月',
month_code string COMMENT '月份编码',
day_month_num string COMMENT '一月第几天',
dim_date_id string COMMENT '日期',
year_week_name_cn string COMMENT '年中第几周',
group_type string COMMENT '分组类型:store,trade_area,city,brand,min_class,mid_class,max_class,all',
city_id string COMMENT '城市id',
city_name string COMMENT '城市name',
trade_area_id string COMMENT '商圈id',
trade_area_name string COMMENT '商圈名称',
store_id string COMMENT '店铺的id',
store_name string COMMENT '店铺名称',
brand_id string COMMENT '品牌id',
brand_name string COMMENT '品牌名称',
max_class_id string COMMENT '商品大类id',
max_class_name string COMMENT '大类名称',
mid_class_id string COMMENT '中类id',
mid_class_name string COMMENT '中类名称',
min_class_id string COMMENT '小类id',
min_class_name string COMMENT '小类名称',
-- =======统计=======
-- 销售收入
sale_amt DECIMAL(38,2) COMMENT '销售收入',
-- 平台收入
plat_amt DECIMAL(38,2) COMMENT '平台收入',
-- 配送成交额
deliver_sale_amt DECIMAL(38,2) COMMENT '配送成交额',
-- 小程序成交额
mini_app_sale_amt DECIMAL(38,2) COMMENT '小程序成交额',
-- 安卓APP成交额
android_sale_amt DECIMAL(38,2) COMMENT '安卓APP成交额',
-- 苹果APP成交额
ios_sale_amt DECIMAL(38,2) COMMENT '苹果APP成交额',
-- PC商城成交额
pcweb_sale_amt DECIMAL(38,2) COMMENT 'PC商城成交额',
-- 成交单量
order_cnt BIGINT COMMENT '成交单量',
-- 参评单量
eva_order_cnt BIGINT COMMENT '参评单量comment=>cmt',
-- 差评单量
bad_eva_order_cnt BIGINT COMMENT '差评单量negtive-comment=>ncmt',
-- 配送成交单量
deliver_order_cnt BIGINT COMMENT '配送单量',
-- 退款单量
refund_order_cnt BIGINT COMMENT '退款单量',
-- 小程序成交单量
miniapp_order_cnt BIGINT COMMENT '小程序成交单量',
-- 安卓APP订单量
android_order_cnt BIGINT COMMENT '安卓APP订单量',
-- 苹果APP订单量
ios_order_cnt BIGINT COMMENT '苹果APP订单量',
-- PC商城成交单量
pcweb_order_cnt BIGINT COMMENT 'PC商城成交单量'
)
COMMENT '销售主题宽表'
ROW format delimited fields terminated BY '\t'
stored AS orc tblproperties ('orc.compress' = 'SNAPPY');
sql语句
1.按天来统计
insert into xxxxx.dm_sale
select
t1.dt as date_time,
'date' as time_type ,
t2.year_code ,
t2.year_month ,
t2.month_code ,
t2.day_month_num ,
t2.dim_date_id ,
t2.year_week_name_cn ,
-- 维度
t1.group_type,
t1.city_id ,
t1.city_name ,
t1.trade_area_id ,
t1.trade_area_name,
t1.store_id,
t1.store_name ,
t1.brand_id,
t1.brand_name,
t1.max_class_id,
t1.max_class_name,
t1.mid_class_id,
t1.mid_class_name,
t1.min_class_id,
t1.min_class_name,
-- 指标
t1.sale_amt,
t1.plat_amt,
t1.deliver_sale_amt ,
t1.mini_app_sale_amt ,
t1.android_sale_amt ,
t1.ios_sale_amt ,
t1.pcweb_sale_amt,
t1.order_cnt,
t1.eva_order_cnt ,
t1.bad_eva_order_cnt ,
t1.deliver_order_cnt ,
t1.refund_order_cnt ,
t1.miniapp_order_cnt ,
t1.android_order_cnt,
t1.ios_order_cnt ,
t1.pcweb_order_cnt
from xxxxx.dws_sale_daycount t1
left join xxxx.dim_date t2 on t1.dt = t2.date_code ;
统计分析按周来统计
上卷统计的分析流程
2.按周来统计
15.8.2商品主题统计宽表
主要指标有:下单次数、下单件数、下单金额、被支付次数、被支付件数、被支付金额、被退款次数、被退款件数、被退款金额、被加入购物车次数、被加入购物车件数、被收藏次数、好评数、中评数、差评数。
维度有:商品+日期(总累计值,近30天的数据)。
1.构建DM层商品主题统计宽表(hive中执行):
create table xxx.dm_sku
(
sku_id string comment 'sku_id',
sku_name string comment '商品名称',
order_last_30d_count bigint comment '最近30日被下单次数',
order_last_30d_num bigint comment '最近30日被下单件数',
order_last_30d_amount decimal(38,2) comment '最近30日被下单金额',
order_count bigint comment '累积被下单次数',
order_num bigint comment '累积被下单件数',
order_amount decimal(38,2) comment '累积被下单金额',
payment_last_30d_count bigint comment '最近30日被支付次数',
payment_last_30d_num bigint comment '最近30日被支付件数',
payment_last_30d_amount decimal(38,2) comment '最近30日被支付金额',
payment_count bigint comment '累积被支付次数',
payment_num bigint comment '累积被支付件数',
payment_amount decimal(38,2) comment '累积被支付金额',
refund_last_30d_count bigint comment '最近三十日退款次数',
refund_last_30d_num bigint comment '最近三十日退款件数',
refund_last_30d_amount decimal(38,2) comment '最近三十日退款金额',
refund_count bigint comment '累积退款次数',
refund_num bigint comment '累积退款件数',
refund_amount decimal(38,2) comment '累积退款金额',
cart_last_30d_count bigint comment '最近30日被加入购物车次数',
cart_last_30d_num bigint comment '最近30日被加入购物车件数',
cart_count bigint comment '累积被加入购物车次数',
cart_num bigint comment '累积被加入购物车件数',
favor_last_30d_count bigint comment '最近30日被收藏次数',
favor_count bigint comment '累积被收藏次数',
evaluation_last_30d_good_count bigint comment '最近30日好评数',
evaluation_last_30d_mid_count bigint comment '最近30日中评数',
evaluation_last_30d_bad_count bigint comment '最近30日差评数',
evaluation_good_count bigint comment '累积好评数',
evaluation_mid_count bigint comment '累积中评数',
evaluation_bad_count bigint comment '累积差评数'
)
COMMENT '商品主题宽表'
ROW format delimited fields terminated BY '\t'
stored AS orc tblproperties ('orc.compress' = 'SNAPPY');
统计分析:先按照天来统计
2.计算累计值
-- 计算总累计值
select
sku_id,
sku_name,
sum(order_count) as order_count,
sum(order_num) as order_num,
sum(order_amount) as order_amount,
sum(payment_count) as payment_count,
sum(payment_num) as payment_num,
sum(payment_amount) as payment_amount,
sum(refund_count) as refund_count,
sum(refund_num) as refund_num,
sum(refund_amount) as refund_amount,
sum(cart_count) as cart_count,
sum(cart_num) as cart_num,
sum(favor_count) as favor_count,
sum(evaluation_good_count) as evaluation_good_count,
sum(evaluation_mid_count) as evaluation_mid_count,
sum(evaluation_bad_count) as evaluation_bad_count
from xxxx.dws_sku_daycount
group by sku_id ,sku_name ;
3.计算近30天
-- 计算总累计值
select
sku_id,
sku_name,
sum(order_count) as order_last_30d_count,
sum(order_num) as order_last_30d_num,
sum(order_amount) as order_last_30d_amount,
sum(payment_count) as payment_last_30d_count,
sum(payment_num) as payment_last_30d_num,
sum(payment_amount) as payment_last_30d_amount,
sum(refund_count) as refund_last_30d_count,
sum(refund_num) as refund_last_30d_num,
sum(refund_amount) as refund_last_30d_amount,
sum(cart_count) as cart_last_30d_count,
sum(cart_num) as cart_last_30d_num,
sum(favor_count) as favor_last_30d_count,
sum(evaluation_good_count) as evaluation_last_30d_good_count,
sum(evaluation_mid_count) as evaluation_last_30d_mid_count,
sum(evaluation_bad_count) as evaluation_last_30d_bad_count
from xxxx.dws_sku_daycount where dt between '2021-07-01' and '2021-07-31'
group by sku_id ,sku_name ;
-- 如果动态获取最近30天的数据呢? 最近30天范围: 上一天日期 ~ 上一天的前30天
select * from hive.bj59_yp_dws_jiale.dws_sku_daycount where dt
between date_format(date_add('day',-30,date_add('day',-1,now())), '%Y-%m-%d') and date_format(date_add('day',-1,now()), '%Y-%m-%d')
-- 思考: 如何获取当前的时间呢?
select NOW();
-- 第二个思考: 如果让这个时间 -30天呢?
select date_add('day',-30,date_add('day',-1,now()))
-- 或者
select date_add('day',-31,now())
-- 第三个: 如何获取时间的 年 月 日部分
select date_