【新】数据仓库分层建模实战指南:从混乱到有序的工程实践

数据仓库分层建模实战指南:从混乱到有序的工程实践

版本:V1.0

适合人群:数据开发工程师、ETL工程师、BI开发工程师、有一定SQL基础的数据从业者


写在前面:你是不是也遇到过这些问题?

如果你做过数据仓库开发,大概率遇到过下面这些场景:

  • 老板让你查一个数据,你翻了半天发现有三张表都叫"用户订单表",但字段还不一样,不知道该用哪张
  • 接手的数仓项目,表与表之间互相依赖,改了一张表,下游十几张表全挂了
  • 业务方要一个报表,你发现需要JOIN七八张表,SQL写得又长又慢,跑一次要半小时
  • 新来的同事问你"这张表是干嘛的",你看了半天DDL也说不清楚
  • 每次大促或者业务改版,数仓就要大改一次,加班到深夜

如果你中了两条以上,别担心,这不是你一个人的问题------这是绝大多数数据仓库都会经历的"成长阵痛"。

这篇文章不跟你讲太多理论,就用大白话 + 实际代码案例,带你搞清楚一件事:怎么建一个不乱、好维护、跑得快的数据仓库。


一、先搞明白:业务库和数仓库是两码事

1.1 一个生活中的比喻

想象你要开一家餐厅:

  • 业务库 就像厨房------厨师在这里切菜、炒菜、装盘,动作要快,来一个菜做一个菜
  • 数仓库 就像财务室------每天晚上要统计今天卖了多少菜、哪些菜卖得好、哪个时段客人最多,目的是算账和做决策

厨房和财务室能混在一起吗?显然不能。厨房要的是快,财务室要的是准。

1.2 技术上的区别

对比项 业务库(MySQL/PostgreSQL) 数仓库(Hive/ClickHouse/Doris)
用来干嘛 支撑APP/网站正常运作(用户登录、下单、支付) 做报表、分析数据、喂给算法模型
查询特点 一次查几条数据,要求毫秒级返回 一次扫几百万行,算个汇总指标
数据更新 频繁增删改,数据随时在变 基本只追加,改得很少
表设计 拆得很细,避免数据重复(比如用户信息和订单分开) 经常合并成宽表,减少JOIN

最常见的坑 :很多新人做数仓,直接把业务库的表结构搬过来,然后发现查起来特别慢、SQL特别难写。原因就在于------业务库的设计思路跟数仓完全不一样


二、数仓为什么要分层?

2.1 不分层的数仓长什么样

假设你接手了一个没有分层的数仓,所有表都堆在一起,大概是这样:

复制代码
ads_user_order_report          -- 用户订单报表
ods_user                       -- 用户原始数据
dwd_order_detail               -- 订单明细(但没人记得这是哪层的)
temp_20240301                  -- 临时表,不知道谁建的
user_order_amount_summary      -- 用户订单金额汇总
ods_order                      -- 订单原始数据
report_daily_gmv               -- 每日GMV报表
... 还有200多张表

问题很明显:

  1. 找不到表------不知道哪张表能用,只能到处问
  2. 不敢改表------不知道下游谁在用,改了可能出事故
  3. 重复造轮子------A团队建了一张"用户订单表",B团队又建了一张"用户交易表",其实是一回事
  4. 数据对不上------同一张报表,不同的人查出来的数不一样

2.2 分层就像给仓库贴标签

分层的核心思想很简单:把数据按"加工程度"分成不同的层级,每一层只做一件事。

常见的分层结构:

每一层做什么、不做什么,必须规定清楚。 这就是本文要讲的核心。


三、核心问题:DWS层到底该放在哪?

3.1 一个真实的冲突案例

假设业务方提了个需求:要做一张"用户交易状态汇总表",方便分析用户下单情况。

这时候会出现两种思路:

思路A(数仓工程师的视角):

"DWD层已经有订单明细表了,DWS层也有用户聚合表,直接JOIN一下不就行了?不用新建表。"

sql 复制代码
-- 思路A:复用现有表,通过JOIN获取数据
SELECT
    u.user_id,
    u.user_name,
    u.register_date,
    COUNT(o.order_id) AS order_count,
    SUM(o.pay_amount) AS total_amount
FROM dwd_user_info u
LEFT JOIN dwd_order_detail o ON u.user_id = o.user_id
GROUP BY u.user_id, u.user_name, u.register_date;

思路B(数据分析师的视角):

"我要一张宽表,所有字段都在一张表里,我直接SELECT就行,不用每次写JOIN。"

sql 复制代码
-- 思路B:直接查宽表
SELECT
    user_id,
    user_name,
    register_date,
    order_count,
    total_amount,
    last_order_date,
    avg_order_amount
FROM ads_user_order_summary
WHERE dt = '2025-05-01';

谁对谁错?

其实都没错,但诉求不一样

角色 核心诉求 想要的方案
数仓工程师 表越少越好、复用率越高越好 思路A(复用现有表)
数据分析师 SQL越简单越好、查询越快越好 思路B(建宽表)

问题的根源:传统做法把DWS层既当"公共汇总层"又当"分析消费层",结果就是------数仓工程师觉得DWS应该规范、可复用,分析师觉得DWS应该好用、查询快。两边打架。

3.2 解决方案:把DWS"搬"到集市层

核心思路就一句话:DWD层只管"把数据洗干净、标准化",集市层(DWS+ADS)只管"让分析的人好用"。

改完之后,分层结构变成这样:

关键变化:

  1. DWD层做减法:不再往DWD层塞各种分析字段,DWD只保留业务最核心的明细数据
  2. 集市层做加法:DWS和ADS合并,按使用场景分成不同的"集市",每个集市独立建设
  3. 各管各的:DWD层由数仓团队统一维护,集市层可以按场景分配给不同的人负责

四、DWD层怎么建:把数据"洗干净、标准化"

4.1 DWD层的核心任务

DWD层(Data Warehouse Detail)是数仓的"地基"。地基打不好,上面盖什么都会歪。

DWD层要做的事:

  • 把ODS层的原始数据清洗(去重、补全、格式统一)
  • 把不同来源的同类数据统一口径(比如APP端和PC端的订单合并成一张表)
  • 把业务含义标准化 (比如状态码1/2/3翻译成待支付/已支付/已发货

DWD层不要做的事:

  • ❌ 不要为了某个报表的需求,往DWD表里加字段
  • ❌ 不要做高度聚合(比如"每个用户每月的总金额"这种放到集市层去做)
  • ❌ 不要直接给业务方查,DWD层是给下游层用的

4.2 一个实际的DWD表设计案例

假设业务库有两张表:

业务库的user表:

id name phone city_id create_time status
1001 张三 13800138000 110100 2024-01-15 1
1002 李四 13900139000 310100 2024-02-20 1

业务库的city表:

city_id city_name province_id
110100 北京 110000
310100 上海 310000

DWD层的设计(用户明细表):

sql 复制代码
CREATE TABLE dwd_user_info_df (
    user_id          BIGINT       COMMENT '用户ID',
    user_name        STRING       COMMENT '用户姓名',
    phone            STRING       COMMENT '手机号',
    city_id          STRING       COMMENT '城市ID',
    city_name        STRING       COMMENT '城市名称(从city表关联过来)',
    province_id      STRING       COMMENT '省份ID',
    register_date    STRING       COMMENT '注册日期',
    user_status      STRING       COMMENT '用户状态:active-正常/inactive-注销',
    etl_time         STRING       COMMENT '数据同步时间'
)
COMMENT '用户明细表-每日全量'
PARTITIONED BY (dt STRING COMMENT '分区日期')
STORED AS PARQUET;

关键设计点:

  1. 关联了city表:把城市名称直接放到DWD表里,下游不用每次都JOIN
  2. 状态码翻译1active0inactive,让人一眼能看懂
  3. 加了分区:按天分区,查询时只扫指定分区,效率高
  4. 用了Parquet格式:列式存储,压缩比高,查询快

4.3 DWD层设计的"三要三不要"

要做 不要做
✅ 统一数据口径(比如金额统一用元还是分) ❌ 为某个报表加专属字段
✅ 关联常用的维度表(城市、类目等) ❌ 做高度聚合(按用户/按天汇总)
✅ 数据清洗(去重、补默认值、格式统一) ❌ 直接暴露给业务方查询

五、集市层怎么建:按场景"各做各的"

5.1 集市层的核心思路

集市层的核心思路就一句话:谁用、怎么用,就怎么建。

不同角色对数据的需求完全不同:

  • 老板看报表:要的是"今天卖了多少钱""跟昨天比涨了还是跌了" → 需要按天汇总的指标表
  • 运营做活动:要的是"最近30天买过东西的用户" → 需要用户维度的行为标签表
  • 风控查风险:要的是"同一个IP下了多少单" → 需要设备/IP维度的聚合表
  • 算法训练模型:要的是"用户过去7天的行为序列" → 需要特征宽表

如果把这些需求都塞到一张表里,这张表会有几百个字段,没人看得懂。 所以必须按场景拆分。

5.2 经营分析集市案例

需求:老板每天要看GMV、订单量、客单价等核心指标。

DWS层设计(按天汇总):

sql 复制代码
CREATE TABLE dws_trade_platform_1d (
    dt                STRING    COMMENT '日期',
    gmv_amount        DECIMAL(18,2)  COMMENT 'GMV金额',
    gmv_cnt           BIGINT    COMMENT 'GMV订单数',
    pay_amount        DECIMAL(18,2)  COMMENT '支付金额',
    pay_cnt           BIGINT    COMMENT '支付订单数',
    refund_amount     DECIMAL(18,2)  COMMENT '退款金额',
    refund_cnt        BIGINT    COMMENT '退款订单数',
    avg_order_amount  DECIMAL(18,2)  COMMENT '客单价'
)
COMMENT '交易核心指标-天粒度'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

ADS层设计(给看板用):

sql 复制代码
CREATE TABLE ads_dashboard_daily (
    dt                    STRING    COMMENT '日期',
    gmv_amount            DECIMAL(18,2)  COMMENT '今日GMV',
    gmv_amount_yesterday  DECIMAL(18,2)  COMMENT '昨日GMV',
    gmv_amount_last7d_avg DECIMAL(18,2)  COMMENT '近7日平均GMV',
    gmv_amount_yoy        DECIMAL(10,4)  COMMENT '同比增速',
    gmv_amount_dod        DECIMAL(10,4)  COMMENT '环比增速',
    pay_user_cnt          BIGINT    COMMENT '支付用户数'
)
COMMENT '经营看板-每日核心指标'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

分层逻辑:

复制代码
DWD订单明细表
    │
    ▼
DWS按天汇总(GMV、订单量、支付金额等)
    │
    ▼
ADS计算同比环比(给看板直接展示)

5.3 用户运营集市案例

需求:运营要能按各种条件圈选用户(比如"北京、最近30天下过单、客单价大于200的女性用户")。

DWS用户维度汇总(天粒度):

sql 复制代码
CREATE TABLE dws_user_trade_1d (
    user_id              BIGINT       COMMENT '用户ID',
    dt                   STRING       COMMENT '日期',
    order_cnt            BIGINT       COMMENT '当日订单数',
    order_amount         DECIMAL(18,2) COMMENT '当日订单金额',
    pay_cnt              BIGINT       COMMENT '当日支付订单数',
    pay_amount           DECIMAL(18,2) COMMENT '当日支付金额',
    first_order_time     STRING       COMMENT '首次下单时间',
    last_order_time      STRING       COMMENT '末次下单时间'
)
COMMENT '用户交易行为-天粒度'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

DWS用户维度汇总(长周期):

sql 复制代码
CREATE TABLE dws_user_trade_nd (
    user_id              BIGINT       COMMENT '用户ID',
    dt                   STRING       COMMENT '统计日期',
   近7天order_cnt        BIGINT       COMMENT '近7天订单数',
   近7天order_amount     DECIMAL(18,2) COMMENT '近7天订单金额',
   近30天order_cnt       BIGINT       COMMENT '近30天订单数',
   近30天order_amount    DECIMAL(18,2) COMMENT '近30天订单金额',
    近90天order_cnt       BIGINT       COMMENT '近90天订单数',
    近90天order_amount    DECIMAL(18,2) COMMENT '近90天订单金额',
    total_order_cnt      BIGINT       COMMENT '历史总订单数',
    total_order_amount   DECIMAL(18,2) COMMENT '历史总订单金额',
    last_order_date      STRING       COMMENT '最近下单日期'
)
COMMENT '用户交易行为-多周期汇总'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

ADS用户标签表(给运营直接用):

sql 复制代码
CREATE TABLE ads_user_profile_tags (
    user_id              BIGINT       COMMENT '用户ID',
    dt                   STRING       COMMENT '日期',
    city_name            STRING       COMMENT '所在城市',
    gender               STRING       COMMENT '性别',
    age_group            STRING       COMMENT '年龄段',
    is_vip               BOOLEAN      COMMENT '是否VIP',
    is_active_30d        BOOLEAN      COMMENT '近30天是否活跃(下过单)',
    is_high_value        BOOLEAN      COMMENT '是否高价值用户(近90天消费>1000)',
    order_frequency      STRING       COMMENT '下单频率:low/medium/high',
    avg_order_amount     DECIMAL(18,2) COMMENT '平均客单价'
)
COMMENT '用户画像标签表'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

运营圈选用户时,SQL就很简单了:

sql 复制代码
-- 运营要圈选:北京、近30天下过单、高价值的女性用户
SELECT user_id
FROM ads_user_profile_tags
WHERE dt = '2025-05-01'
  AND city_name = '北京'
  AND gender = 'female'
  AND is_active_30d = true
  AND is_high_value = true;

5.4 集市层设计的核心原则

原则 说明 反例
按场景建表 经营分析、用户运营、风控分析各建各的 一张表塞几百个字段,什么场景都用
粒度明确 每张表必须有明确的粒度(按天/按用户/按订单) 一张表里既有天粒度又有用户粒度
字段分组 相关字段放一起,命名有规律 字段名乱七八糟,看不出含义
控制字段数 单表建议不超过200个字段 一张表500+字段,没人看得完

六、实战:从需求到落地的完整流程

6.1 接到需求后的思考顺序

当你接到一个数据需求时,按这个顺序思考:

6.2 一个完整的需求落地案例

需求描述:运营团队需要一张表,能看到每个用户的基本信息、历史订单情况、最近一次下单时间,用于做用户分层运营。

第一步:判断归属

这是运营圈人用的 → 归属用户运营集市

第二步:确定粒度

按用户维度 → 粒度是用户

第三步:确定数据来源

  • 用户基本信息 → dwd_user_info_df
  • 订单信息 → dwd_order_detail_di
  • 需要关联 → 通过user_id关联

第四步:设计表结构

sql 复制代码
-- 集市层:用户运营-用户分层宽表
CREATE TABLE ads_user_operation_segment (
    user_id              BIGINT       COMMENT '用户ID',
    dt                   STRING       COMMENT '日期',
    
    -- 用户基本信息
    user_name            STRING       COMMENT '用户姓名',
    phone                STRING       COMMENT '手机号',
    city_name            STRING       COMMENT '城市',
    register_date        STRING       COMMENT '注册日期',
    
    -- 订单统计
    total_order_cnt      BIGINT       COMMENT '历史总订单数',
    total_order_amount   DECIMAL(18,2) COMMENT '历史总订单金额',
    last_order_date      STRING       COMMENT '最近下单日期',
    days_since_last_order INT         COMMENT '距上次下单天数',
    
    -- 用户分层标签
    user_level           STRING       COMMENT '用户等级:S/A/B/C'
)
COMMENT '用户运营-用户分层宽表'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

第五步:写SQL

sql 复制代码
INSERT OVERWRITE TABLE ads_user_operation_segment PARTITION (dt = '${bizdate}')
SELECT
    u.user_id,
    '${bizdate}' AS dt,
    u.user_name,
    u.phone,
    u.city_name,
    u.register_date,
    COALESCE(o.total_order_cnt, 0) AS total_order_cnt,
    COALESCE(o.total_order_amount, 0) AS total_order_amount,
    o.last_order_date,
    DATEDIFF('${bizdate}', o.last_order_date) AS days_since_last_order,
    CASE
        WHEN COALESCE(o.total_order_amount, 0) >= 5000 THEN 'S'
        WHEN COALESCE(o.total_order_amount, 0) >= 2000 THEN 'A'
        WHEN COALESCE(o.total_order_amount, 0) >= 500  THEN 'B'
        ELSE 'C'
    END AS user_level
FROM dwd_user_info_df u
LEFT JOIN (
    SELECT
        user_id,
        COUNT(order_id) AS total_order_cnt,
        SUM(pay_amount) AS total_order_amount,
        MAX(dt) AS last_order_date
    FROM dwd_order_detail_di
    GROUP BY user_id
) o ON u.user_id = o.user_id;

第六步:验证

  • 数据量对不对?(跟业务库对比一下总数)
  • 金额对不对?(抽几个用户手动算一下)
  • 跑得快不快?(看执行计划,有没有数据倾斜)

七、避坑指南:数仓开发最常见的10个坑

坑1:直接从ODS层取数,跳过DWD层

sql 复制代码
-- ❌ 错误做法:直接从ODS取数
SELECT * FROM ods_order WHERE dt = '2025-05-01';

-- ✅ 正确做法:从DWD层取数
SELECT * FROM dwd_order_detail_di WHERE dt = '2025-05-01';

为什么不行? ODS层是原始数据,可能有脏数据、口径不统一。DWD层已经做了清洗和标准化,从DWD取才能保证数据质量。

坑2:DWD层塞了太多分析字段

sql 复制代码
-- ❌ 错误做法:DWD表里加了"近30天订单数"这种分析字段
CREATE TABLE dwd_user_info_df (
    user_id BIGINT,
    user_name STRING,
    order_cnt_30d BIGINT,  -- 这个不该出现在DWD层!
    ...
);

为什么不行? DWD层应该是"业务事实的标准化","近30天订单数"是分析结果,应该放在集市层。DWD层加了分析字段后,这张表就不再是"明细"了,而且每次更新都要重新算30天,效率极低。

坑3:一张表塞所有场景的字段

sql 复制代码
-- ❌ 错误做法:一张表有300个字段,经营分析、风控、算法全在里面
CREATE TABLE ads_user_all_in_one (
    user_id BIGINT,
    -- 经营分析字段
    total_order_amount DECIMAL(18,2),
    -- 风控字段
    ip_cnt_7d BIGINT,
    device_cnt_7d BIGINT,
    -- 算法字段
    feature_1 DOUBLE,
    feature_2 DOUBLE,
    ...  -- 总共300个字段
);

为什么不行? 不同场景的更新频率不同(经营分析T+1、风控可能要求实时)、存储需求不同(算法特征需要保留历史版本)。混在一起只会互相拖累。

坑4:粒度不明确

sql 复制代码
-- ❌ 错误做法:一张表里既有天粒度又有用户粒度
CREATE TABLE dws_user_order (
    user_id BIGINT,
    dt STRING,
    order_cnt_1d BIGINT,     -- 天粒度
    order_cnt_30d BIGINT,    -- 月粒度
    total_order_cnt BIGINT   -- 历史粒度
);

为什么不行? 天粒度表应该每天全量更新,历史粒度表只需要增量更新。混在一起会导致更新逻辑复杂、存储浪费。应该拆成三张表。

坑5:表之间循环依赖

复制代码
表A → 依赖表B的数据
表B → 依赖表C的数据
表C → 依赖表A的数据  ← 循环了!

怎么避免? 严格遵守分层规范:ODS→DWD→DWS→ADS,只能从上往下依赖,不能反过来,也不能绕圈。

坑6:不建分区

sql 复制代码
-- ❌ 错误做法:不建分区,每次查询全表扫描
CREATE TABLE dwd_order_detail (
    order_id BIGINT,
    user_id BIGINT,
    ...
);

-- ✅ 正确做法:按天分区
CREATE TABLE dwd_order_detail_di (
    order_id BIGINT,
    user_id BIGINT,
    ...
)
PARTITIONED BY (dt STRING);

为什么重要? 不建分区的话,查一天的数据也要扫全表,效率差几十倍甚至上百倍。

坑7:不写注释

sql 复制代码
-- ❌ 错误做法:字段名看不懂,还没有注释
CREATE TABLE t1 (
    c1 BIGINT,
    c2 STRING,
    c3 INT
);

-- ✅ 正确做法:表名、字段名、字段含义都要清楚
CREATE TABLE dwd_order_detail_di (
    order_id BIGINT COMMENT '订单ID',
    user_id BIGINT COMMENT '下单用户ID',
    pay_amount DECIMAL(18,2) COMMENT '实付金额(元)'
);

坑8:临时表不清理

很多团队都有这种表:temp_xxxtest_xxxbackup_xxxxxx_20240301。建的时候说"用完就删",结果用了一年还在。

怎么解决?

  • 临时表命名加前缀:tmp_用户名_日期_用途
  • 设置生命周期:临时表最多保留7天,到期自动删除
  • 定期清理:每月做一次临时表清理

坑9:不控制数据倾斜

sql 复制代码
-- ❌ 容易倾斜的JOIN:某个key的数据量特别大
SELECT * FROM dwd_order_detail o
LEFT JOIN dwd_blacklist_user b ON o.user_id = b.user_id;
-- 如果黑名单里有几百万"刷单账号",这些账号的订单会全堆到一个reduce上

怎么解决?

  • JOIN前过滤掉已知的"大key"
  • 使用MapJoin(小表JOIN大表时)
  • 对倾斜key加随机前缀打散

坑10:不监控数据质量

数据跑完了≠数据是对的。常见的数据质量问题:

  • 数据量突增/突降:昨天100万条,今天10万条 → 可能数据没同步过来
  • 主键重复:同一张表里出现了两条相同主键的数据 → 可能去重逻辑有问题
  • 金额异常:出现了负数金额或者超大金额 → 可能数据源有问题
  • 空值率突增:某个字段昨天99%有值,今天50%是空 → 可能上游改了字段

最低限度的监控:

sql 复制代码
-- 每天跑完数据后,检查这几个指标
SELECT
    dt,
    COUNT(*) AS total_cnt,                    -- 总行数
    COUNT(DISTINCT user_id) AS unique_user_cnt, -- 去重用户数
    COUNT(CASE WHEN order_id IS NULL THEN 1 END) AS null_order_cnt  -- 空值数
FROM dwd_order_detail_di
WHERE dt >= DATE_SUB(CURRENT_DATE(), 7)
GROUP BY dt;

八、治理:怎么让数仓不乱下去

8.1 最简单的治理规则

不需要复杂的工具,先做到以下五条:

规则 说明 违反后果
不能反向依赖 DWD不能依赖DWS/ADS的数据 分层乱了
不能跨层取数 ADS只能依赖DWS,不能直接取DWD 中间层失去意义
不能从ODS直连 必须经过DWD清洗后再用 数据口径不统一
表名要规范 前缀标明层级:ods_/dwd_/dws_/ads_ 找不到表
字段要注释 每个字段必须有COMMENT 没人知道字段含义

8.2 定期做"大扫除"

就像家里要定期打扫一样,数仓也要定期清理:

每月做一次:

  • 找出一周内没有被查询过的表 → 确认是否可以删除
  • 找出临时表 → 超过7天的删除
  • 找出重复的表(名字不同但数据一样)→ 合并或删除

每季度做一次:

  • 检查各层的表数量占比是否合理
  • 检查存储成本最高的20张表 → 优化或下线
  • 检查数据质量监控的告警记录 → 修复反复出现的问题

8.3 怎么判断数仓"乱不乱"

几个简单的指标:

指标 健康值 危险值
表名带前缀的比例 >95% <80%
有字段注释的比例 >90% <70%
临时表占总表数的比例 <5% >15%
一周无查询的表占比 <10% >25%
跨层依赖占比 <5% >15%

九、总结:记住这三句话就够了

如果你看完了全文,但觉得内容太多记不住,那就记住这三句话:

第一句:DWD层只管"洗干净"

DWD层就是做数据清洗和标准化。把业务库的原始数据洗干净、统一口径、关联常用维度,然后交给下游。不要在DWD层做分析、不要为了某个报表加字段。

第二句:集市层只管"好用"

集市层(DWS+ADS)就是让用数据的人方便。经营分析要什么就建什么,用户运营要什么就建什么,按场景拆分,不要混在一起

第三句:分层规范就是"交通规则"

红灯停、绿灯行,看起来是限制,其实是保证所有人都不堵车。数仓的分层规范也一样------ODS→DWD→DWS→ADS,只能从上往下,不能反过来。做到了这一点,数仓就不会乱。


附录:常用SQL模板

A1. DWD层数据同步模板

sql 复制代码
-- 每日增量同步(适用于有更新时间的表)
INSERT OVERWRITE TABLE dwd_table_name_di PARTITION (dt = '${bizdate}')
SELECT
    id,
    field1,
    field2,
    -- 统一金额单位(假设业务库是分,这里转成元)
    amount_cent / 100.0 AS amount_yuan,
    -- 统一状态码
    CASE status
        WHEN 1 THEN 'active'
        WHEN 2 THEN 'inactive'
        ELSE 'unknown'
    END AS status_name,
    -- 补全默认值
    COALESCE(city_id, '-1') AS city_id,
    CURRENT_TIMESTAMP() AS etl_time
FROM ods_table_name
WHERE dt = '${bizdate}'
  AND is_deleted = 0;  -- 过滤已删除的数据

A2. DWS层汇总模板

sql 复制代码
-- 天粒度汇总
INSERT OVERWRITE TABLE dws_table_name_1d PARTITION (dt = '${bizdate}')
SELECT
    user_id,
    '${bizdate}' AS dt,
    COUNT(order_id) AS order_cnt,
    SUM(pay_amount) AS order_amount,
    COUNT(DISTINCT product_id) AS product_cnt,
    MIN(create_time) AS first_order_time,
    MAX(create_time) AS last_order_time
FROM dwd_order_detail_di
WHERE dt = '${bizdate}'
GROUP BY user_id;

A3. 数据质量检查模板

sql 复制代码
-- 插入数据前检查源表数据量
-- 如果源表数据量为0,说明数据没同步过来,直接退出
SELECT COUNT(*) FROM ods_source_table WHERE dt = '${bizdate}';
-- 如果结果为0,则不执行后续SQL

最后的话 :数仓建设不是一锤子买卖,而是一个持续迭代的过程。不要追求一开始就完美,先做到"有分层、有规范",然后在实践中不断优化。记住:好的数仓是"长"出来的,不是"设计"出来的。

相关推荐
3D霸霸20 小时前
Sourcetree 拉取新工程
数据仓库·unity
Leo.yuan1 天前
企业数字化转型选型指南:FineBI如何助力数据驱动决策?
数据仓库·人工智能·信息可视化
青春万岁!!2 天前
hive 动态分区参数设置错误导致数据不稳定
大数据·数据仓库·hive·hadoop
roman_日积跬步-终至千里3 天前
为什么 Hive 无法通过同步 JDBC 导出百万级数据?
数据仓库·hive·hadoop
roman_日积跬步-终至千里3 天前
Hive JDBC vs MySQL JDBC:**“服务端推完就跑,客户端慢慢吃”**详解
数据仓库·hive·hadoop
AM越.6 天前
助睿:!!零代码解决!!订单利润分流数据加工o(* ̄▽ ̄*)ブ
数据仓库·笔记·etl·助睿
juniperhan7 天前
Flink 系列第24篇:Flink SQL 集成维度表指南:存储选型、参数调优与实战避坑
大数据·数据仓库·sql·flink
RestCloud7 天前
ETL数据质量保障:如何通过优化提升数据准确性?
数据仓库·etl·数据处理·数据传输·数据同步·数据集成平台
2501_927283589 天前
荣联汇智助力天津艺虹打造“软硬一体”智慧工厂,全流程自动化引领印刷包装行业数智变革
大数据·运维·数据仓库·人工智能·低代码·自动化