离线数仓(七)【DIM 层开发】

前言

        今天开始 DIM 层的开发,说开发好像有点不配,还只是学习阶段,离开发还有很长的路要走。

一个人想象自己不懂得的事很容易浪漫。                                --《沉默的大多数》王小波

1、DIM 层开发

DIM层设计要点:

  1. DIM层的设计依据是维度建模理论,该层存储维度模型的维度表。
  2. DIM层的数据存储格式为orc列式存储+snappy压缩。
  3. DIM层表名的命名规范为dim_表名_全量表或者拉链表标识(full/zip)

        我们在前面 ODS 层的开发时数据压缩用的是 gz ,为的是最大的压缩存储。但是这里,我们的 DIM 、DWD、DWS 层是之后用的最频繁的三层,所以我们必须保证它的读取和解压缩更快,所以我们选用 orc + snappy 压缩。

        维度表我们大致可以分为两类:每日全量快照表和拉链表

DIM 层就是创建维度表的过程,所以我们需要先回顾一下维度建模的知识:

1.1、维度建模回顾

        前面我们学习数据仓库构建流程的时候说 DWD 和 DIM 层是业务驱动的,因为我们的事实表(存在 DWD 层)取决于业务系统中业务过程,而我们的维度表(存在 DIM 层)取决于业务系统中的环境,它俩都和我们的指标没啥关系。

        在设计维度模型之前我们需要构建业务总线矩阵,矩阵的行是一个个业务过程(具体说就是每行代表一个业务事实表),矩阵的列是一个个的维度(代表一个维度表),行列的交点表示业务过程与维度存在关联关系。

         按照事务型事实表的设计流程我们就可以得到业务总线矩阵,因为我们的维度模型是以事实表为核心,而事实表主要就是事务型事实表。

        这里只对红色框起来的部分维度进行建表,而蓝色框的维度我们会做一个维度退化。回顾一下维度退化的定义:

        如果某些维度表的维度属性很少(比如支付方式表没有必要去单独创建一个维度表,因为它就一个支付方式字段),则可不创建该维度表,而把该表的维度属性直接增加到与之相关的事实表中,这个操作称为维度退化。

        也就是说,我们直接把支付方式这个维度属性作为一个字段加到支付成功和退款成功这两个事实表当中,把退单类型作为一个字段做到退款成功这个事实表当中...

        还有渠道、设备这两个维度属性也要做维度退化,尽管它俩和多张事实表有关系,但是与这两个维度属性相关联的业务过程基本都是日志当中的业务过程。

CREATE EXTERNAL TABLE ods_log_inc
(`common`   STRUCT<ar :STRING,ba :STRING,ch :STRING,is_new :STRING,md :STRING,mid :STRING,os :STRING,uid :STRING,vc:STRING> COMMENT '公共信息',`page`     STRUCT<during_time :STRING,item :STRING,item_type :STRING,last_page_id :STRING,page_id:STRING,source_type :STRING> COMMENT '页面信息',`actions`  ARRAY<STRUCT<action_id:STRING,item:STRING,item_type:STRING,ts:BIGINT>> COMMENT '动作信息',`displays` ARRAY<STRUCT<display_type :STRING,item :STRING,item_type :STRING,`order` :STRING,pos_id:STRING>> COMMENT '曝光信息',`start`    STRUCT<entry :STRING,loading_time :BIGINT,open_ad_id :BIGINT,open_ad_ms :BIGINT,open_ad_skip_ms:BIGINT> COMMENT '启动信息',`err`      STRUCT<error_code:BIGINT,msg:STRING> COMMENT '错误信息',`ts`       BIGINT  COMMENT '时间戳'
) COMMENT '活动信息表'PARTITIONED BY (`dt` STRING)ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.JsonSerDe'LOCATION '/warehouse/gmall/ods/ods_log_inc/';

        我们在创建日志表的时候已经把业务过程和维度属性给放到一起了,没有必要再把维度属性 "渠道"、"设备" 再拆出来单独建个维度表。

1.2、维度表设计步骤

具体建模步骤看前面的博客:数据仓库(五)【数据仓库建模】

1.2.1、确认维度

        也就确认创建哪些维度表,哪些维度属性不需要创建单独的维度表而是做维度退化,我们上面都分析清楚了。

1.2.2、确定主维表和相关维表

         主维表和相关维表指的都是和维度相关的业务系统当中的表,然后通常情况下粒度最细的是主维表(比如商品这个维度,我们要去业务系统中找到和它相关的表,发现有购物车表、订单表、商品信息表、退单表... ),如果我们以订单表作为主维表,那一个订单中可能有多个订单,那就导致粒度太大,我们无法确定商品的多个属性。所以我们尽可能选择粒度最细的,比如商品信息表,从商品信息表中我们可以得到更多和商品这个维度相关的属性。

1.2.3、确定维度属性

        确定维度属性也就是确定维度表字段。维度属性可直接从主维表或相关维表中选择,也可通过进一步加工得到。

1.3、商品维度表设计

1.3.1、确认维度

        我们在上面回顾维度建模的时候已经确定维度了 ,准确说应该是给事实表确定维度,我对这一步的理解是先清楚事实表需要这个维度我们才需要继续创建这张维度表。

1.3.2、确定主维表和相关维表

        我们需要去业务系统寻找和商品相关的每张表:

        我们的 base_attr_info 存储的是平台属性名:品牌、分类、内存、材质、CPU型号等都是平台属性名,而 base_attr_value 存储的三星、6.4寸、8G内存、玻璃材质、骁龙888 等都是平台属性值。

一个平台属性值可能对应多个商品,同样一个商品也有很多个平台属性值,它们是多对多的关系,平台属性和商品信息的关系存储在 sku_attr_value 这个中间表中。

        上面都是我们和平台属性有关的表,我们现在需要知道从应该这些表中获取哪些和商品相关的信息。其实我们主要希望得到的就是每个商品对应的平台属性名(attr_id 和 value_name)和平台属性值(value_id 和 value_name),而我们需要的这些字段都正好被存在了 sku_attr_value 这张中间表,这张表并没有按照三范式去设计,所以我们都不需要去通过 attr_id 和 value_id 去关联另外两张表去获取平台属性名和平台属性值。所以我们之前在做数据采集的时候其实也并没有对这两张表(base_attr_value 和 base_attr_info )进行同步。

        和平台属性一样,一个商品属性值可能对应多个商品,同样一个商品也有很多个销售属性值,它们同样是多对多的关系,商品属性和商品信息的关系存储在 sku_sale_attr_value 这个中间表中。而且它同样对 spu_sale_attr 和 spu_sale_attr_value 中的销售属性和销售属性值的字段进行了冗余,我们采集的时候也没有进行同步,而且在设计维度表的时候直接参 sku_sale_attr_value 这个中间表即可。

        这样,我们的主维表和相关维表就确定好了,sku_info 是最细粒度的表,所以 sku_info 就是主维表,其它和商品相关的表都是相关维表。

1.3.3、确定商品维度的属性

        确定商品维度属性就是确定商品维度表的字段,商品主维表(sku_info)具有最细粒度的属性,所以主维表的字段我们可以直接作为商品维度表的维度属性,而其它的相关维表只需要取出它的 id 作为维度属性即可。

1.4、商品维度表的实现

1.4.1、建表语句

DROP TABLE IF EXISTS dim_sku_full;
CREATE EXTERNAL TABLE dim_sku_full
(`id`                   STRING COMMENT 'sku_id',`price`                DECIMAL(16, 2) COMMENT '商品价格',`sku_name`             STRING COMMENT '商品名称',`sku_desc`             STRING COMMENT '商品描述',`weight`               DECIMAL(16, 2) COMMENT '重量',`is_sale`              BOOLEAN COMMENT '是否在售',`spu_id`               STRING COMMENT 'spu编号',`spu_name`             STRING COMMENT 'spu名称',`category3_id`         STRING COMMENT '三级分类id',`category3_name`       STRING COMMENT '三级分类名称',`category2_id`         STRING COMMENT '二级分类id',`category2_name`       STRING COMMENT '二级分类名称',`category1_id`         STRING COMMENT '一级分类id',`category1_name`       STRING COMMENT '一级分类名称',`tm_id`                STRING COMMENT '品牌id',`tm_name`              STRING COMMENT '品牌名称',`sku_attr_values`      ARRAY<STRUCT<attr_id :STRING,value_id :STRING,attr_name :STRING,value_name:STRING>> COMMENT '平台属性',`sku_sale_attr_values` ARRAY<STRUCT<sale_attr_id :STRING,sale_attr_value_id :STRING,sale_attr_name :STRING,sale_attr_value_name:STRING>> COMMENT '销售属性',`create_time`          STRING COMMENT '创建时间'
) COMMENT '商品维度表'PARTITIONED BY (`dt` STRING)STORED AS ORCLOCATION '/warehouse/gmall/dim/dim_sku_full/'TBLPROPERTIES ('orc.compress' = 'snappy');

可以看到,大部分的维度属性是来自于我们的主维表:

剩下的字段来自我们的其它维表,比如 SKU 相关的平台属性和销售属性:

`sku_attr_values`      ARRAY<STRUCT<attr_id :STRING,value_id :STRING,attr_name :STRING,value_name:STRING>> COMMENT '平台属性',`sku_sale_attr_values` ARRAY<STRUCT<sale_attr_id :STRING,sale_attr_value_id :STRING,sale_attr_name :STRING,sale_attr_value_name:STRING>> COMMENT '销售属性'

        其中,sku_attr_values 存的是平台属性名和平台属性值;sku_sale_attr_values 存的是销售属性名和销售属性值。这是我们前面维度表设计中提到的一个设计要点——多值属性,对于多值属性通常有两种存储方案:1. 多个属性值放到一个字段,这也是我们这里用到的。2. 将多值属性放到多个字段,这里我们没有用,因为我们不能确定多值属性的属性个数,所以就无法怎么去设计这个结构体。

        至于这张全量表的分区,我们选择用日期进行分区,每天把前一天的数据都得导入到一个日期目录下。

        这里我们选择 store as orc 来指定存储格式为 orc ,使用 tblproperties 来指定压缩格式为 snappy。

4.1.2、装载语句

        这张表的装载我们不能再用简单的 load 语句了,因为我们现在不再是通过文件映射到表格了。我们得通过 insert + select 来进行装载,select 的当然就是我们的 ODS 层的原始数据表了,而且 insert 时需要指定分区。

        这就需要我们从 ODS 层中和我们商品相关的所有表(主维表和相关维表)中获取了,而且是拿到这些表当天分区的数据,然后写入到当天的维度表中。

-- 数据装载
-- 2020-06-14
-- sku_info 中的数据
selectid,spu_id,price,sku_name,sku_desc,weight,tm_id,category3_id,is_sale,create_time
from ods_sku_info_full
where dt='2020-06-14';-- spu_info 中的数据
selectid,spu_name
from ods_spu_info_full
where dt='2020-06-14';-- base_category3 中的数据,通过category2_id和base_category2产生关联
selectid,name,category2_id
from ods_base_category3_full
where dt='2020-06-14';-- base_category2 中的数据,通过category1_id和base_category1产生关联
selectid,name,category1_id
from ods_base_category2_full
where dt='2020-06-14';selectid,name
from ods_base_category1_full
where dt='2020-06-14';selectid,tm_name
from ods_base_trademark_full
where dt='2020-06-14';selectsku_id,
collect_set(named_struct("attr_id",attr_id,"value_id",value_id,"attr_name",attr_name,"value_name",value_name))
from ods_sku_attr_value_full
where dt='2020-06-14'
group by sku_id;selectsku_id,
collect_set(named_struct('sale_attr_id',sale_attr_id,'sale_attr_value_id',sale_attr_value_id,'sale_attr_name',sale_attr_name,'sale_attr_value_name',sale_attr_value_name)) sale_attrsfrom ods_sku_sale_attr_value_fullwhere dt='2020-06-14'group by sku_id;

对于平台属性名和平台属性值,我们要把同一个 sku_id 的平台属性名和平台属性值放到同一个结构体数组中:

selectsku_id,collect_set(named_struct("attr_id",attr_id,"value_id",value_id,"attr_name",attr_name,"value_name",value_name))
from ods_sku_attr_value_full
where dt='2020-06-14'
group by sku_id;

        销售属性名和销售属性值也是一样;最后我们一共从这 8 张和商品信息相关的表中来获取我们需要的字段,但是我们总不能就这么 join 吧,那可读性太差了!我们学习一种新的语法(CTE:common table expression):

with
sku as
(selectid,price,sku_name,sku_desc,weight,is_sale,spu_id,category3_id,tm_id,create_timefrom ods_sku_info_fullwhere dt='2020-06-14'
),
spu as
(selectid,spu_namefrom ods_spu_info_fullwhere dt='2020-06-14'
),
c3 as
(selectid,name,category2_idfrom ods_base_category3_fullwhere dt='2020-06-14'
),
c2 as
(selectid,name,category1_idfrom ods_base_category2_fullwhere dt='2020-06-14'
),
c1 as
(selectid,namefrom ods_base_category1_fullwhere dt='2020-06-14'
),
tm as
(selectid,tm_namefrom ods_base_trademark_fullwhere dt='2020-06-14'
),
attr as
(selectsku_id,collect_set(named_struct('attr_id',attr_id,'value_id',value_id,'attr_name',attr_name,'value_name',value_name)) attrsfrom ods_sku_attr_value_fullwhere dt='2020-06-14'group by sku_id
),
sale_attr as
(selectsku_id,collect_set(named_struct('sale_attr_id',sale_attr_id,'sale_attr_value_id',sale_attr_value_id,'sale_attr_name',sale_attr_name,'sale_attr_value_name',sale_attr_value_name)) sale_attrsfrom ods_sku_sale_attr_value_fullwhere dt='2020-06-14'group by sku_id
)
insert overwrite table dim_sku_full partition(dt='2020-06-14')
selectsku.id,sku.price,sku.sku_name,sku.sku_desc,sku.weight,sku.is_sale,sku.spu_id,spu.spu_name,sku.category3_id,c3.name,c3.category2_id,c2.name,c2.category1_id,c1.name,sku.tm_id,tm.tm_name,attr.attrs,sale_attr.sale_attrs,sku.create_time
from sku
left join spu on sku.spu_id=spu.id
left join c3 on sku.category3_id=c3.id
left join c2 on c3.category2_id=c2.id
left join c1 on c2.category1_id=c1.id
left join tm on sku.tm_id=tm.id
left join attr on sku.id=attr.sku_id
left join sale_attr on sku.id=sale_attr.sku_id;

        可以看到,我们在 join 这 8 张表的时候,先从我们的主维表(sku)去查询然后再和其它维表去 left join 来保证结果中包含所有的 sku 信息。而且我们可以看到每张表都能直接的(通过和 sku 表中的字段)或者间接的(比如 1级category和2级category产生关联,2级category再和3级category产生关联,最后3级category直接和sku通过公共字段产生关联)和我们的主维表产生关联。

        而且我们这里用的是 insert overwrite,为的是保持任务的幂等性,比如说,我们用 insert overwrite 执行这个任务多少次它的结果都是一样的,也就不用担心执行过程失败重新执行时数据重复的问题;但是如果我们用 insert into 的话,我们这个分区的数据就可能会发生重复。

1.5、优惠券维度表

剩下的明天完成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536187.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第0章 学习包介绍

一、考试介绍 二、考点分析 白盒测试、黑盒测试、面向对象测试、web测试&#xff1b;下午题考的概率很大&#xff1b; 上午题考点&#xff1a; 三、资料包 四、如何学习

计算机网络-第7章 网络安全(2)

7.5 密钥分配 7.6 互联网使用的安全协议 7.7 系统安全&#xff1a;防火墙与入侵检测

java八股文复习----java多线程---2024/03/12

1.并发编程的三要素是什么&#xff1f; 2.java如何创建线程 继承Thread类创建线程 实现Runnable接口创建线程 使用Callable和Future创建线程 有返回值 使用线程池创建线程 代码演示 import java.util.concurrent.*; public class threadTest{public static void main(String[…

基于Ambari搭建大数据分析平台

一、部署工具简介 1. Hadoop生态系统 Hadoop big data ecosystem in Apache stack 2. Hadoop的发行版本 Hadoop的发行版除了Apache的开源版本之外&#xff0c;国外比较流行的还有&#xff1a;Cloudera发行版(CDH)、Hortonworks发行版&#xff08;HDP&#xff09;、MapR等&am…

docker-compose up -d使用遇到问题no configuration file provided: not found

docker-compose up -d使用遇到问题&#xff0c;因为你文件名称没指定&#xff0c; 又找不到默认的文件名称&#xff1b;如果该目录下有个文件叫docker-compose.yml时&#xff0c;那么可以直接使用docker-compose up -d;否则就要使用docker-compose -f mysql up -d

一款开箱即用的 Vue3 中后台管理系统框架

概述 Fantastic-admin是一个基于Vue.js技术栈的后台管理框架&#xff0c;设计用于提升开发效率和优化用户界面。它通常包括了一套完整的前端解决方案&#xff0c;如用户界面组件、工具和库&#xff0c;以支持快速开发高质量的后台产品。这个框架可能强调易用性、灵活性和高性能…

uniapp开发的跳转到小程序

uniapp开发的h5跳转到小程序 https://www.cnblogs.com/xiaojianwei/p/16352698.html官方&#xff1a;使用 URL Scheme 打开小程序 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-scheme.html 链接代码 <a href"weixin://dl/business/…

HBuilder发行微信小程序

首先需要完善mainifest.json中的基本配置 这个需要组测dcloud才可以获取&#xff0c;注册后点击重新获取就可以。 然后发行前还需要完成dcloud的信息&#xff0c;这个他会给你网址 点击连接完成信息填写就可以了 然后就可以发行了。 发行成功后会自动跳转微信小程序&#xff…

AI“造神运动”终结,杀死,重生

AGI回归本质&#xff0c;百亿美金创业机会涌现。 “专注AI技术迭代会让我焦虑&#xff0c;关注业务我不会焦虑&#xff0c;有些问题十年前存在十年后还在&#xff0c;我现在就明确不卷模型&#xff0c;只思考如何让产品能自我‘造血’。” 一位正卷在AI创业洪流里的硅谷创业者…

大数据 - HBase《一》- Hbase基本概念

目录 1.1. Hbase简介 1.2 Hbase,Hive, Mysql对比 1.3 Hbase数据模型 &#x1f959;region(区域) &#x1f959;rowkey(行键) &#x1f959;列族&#xff08;column family) &#x1f959;列&#xff08;column Qualifier) &#x1f959;版本&#xff08;version)-默认按…

大话设计模式——5.代理模式(Proxy Pattern)

1.定义 为其他具体对象提供一种代理用以控制对这个对象的访问&#xff0c;属于结构型模式。 UML图&#xff1a; 2.示例 生活中有许多的代理&#xff0c;如房产中介&#xff0c;房主出售的房子挂在中介处&#xff0c;中介帮忙寻找需要的客户&#xff0c;客户不需要直接接触房…

基于LSTM实现春联上联对下联

按照阿光的项目做出了学习笔记&#xff0c;pytorch深度学习实战项目100例 基于LSTM实现春联上联对下联 基于LSTM&#xff08;长短期记忆网络&#xff09;实现春联上联对下联是一种有趣且具有挑战性的任务&#xff0c;它涉及到自然语言处理&#xff08;NLP&#xff09;中的序列…