大数据

数仓建模体系01

问题:基于上面的讨论,帮我输出一个完整版本的数据建模体系,包含每一层做的具体工作,比如ods到dwd做数据清洗、退化等 列出具体事项

以下是为你量身定制的 高企咨询服务场景下的完整离线数仓建模体系,覆盖从 ODS → DWD → DWS → DWT → ADS 的每一层,明确每层职责、具体工作项、输入输出、关键规范和示例,可直接作为团队交付标准或建模规范文档使用。

🧱 高企咨询数仓建模体系(完整版)

适用场景:企业资质评估、客户画像、销售跟进、资源匹配
核心实体:企业、联系人、专利/软著、联系方式、来源、标签
技术栈:Doris(支持 Array、物化视图、倒排索引)

🗃️ 一、ODS 层(Operational Data Store)—— 贴源层

✅ 定位:

  • 保留原始业务系统数据,不做任何业务逻辑处理
  • 仅做技术性抽取、分区、压缩

🛠️ 具体工作:

事项说明示例
1. 数据接入全量/增量同步源系统表(MySQL/API/文件)ods_company,ods_contact,ods_patent
2. 分区存储按天分区(dt字段),保留365天PARTITION BY dt
3. 保留原始结构字段名、类型、顺序与源系统一致不改gmt_createcreate_time
4. 保留逻辑删除标记不过滤is_deleted=1数据保留脏数据供审计
5. 添加技术字段etl_time,source_system,dt记录抽取时间与来源
6. 数据压缩使用 ZSTD/LZ4 压缩节省存储Doris 默认支持

⚠️ 禁止事项:

  • ❌ 不做字段清洗、标准化
  • ❌ 不做关联、聚合、计算
  • ❌ 不删字段、不改名

🧱 二、DWD 层(Data Warehouse Detail)—— 明细建模层

✅ 定位:

  • 面向分析主题的原子粒度建模
  • 清洗、标准化、退化常用维度、统一口径
  • 为上层提供“干净、可关联、可下钻”的明细数据

🛠️ 具体工作:

  1. 数据清洗 & 过滤
事项说明示例
过滤逻辑删除WHERE is_deleted = 0
处理空值字符串→'',数值→-10,布尔→0/1COALESCE(phone, '')
去重根据业务主键去重(如company_id + update_timeROW_NUMBER() OVER (PARTITION BY ...)
异常值处理过滤非法值(如手机号长度≠11、邮箱无@)WHERE LENGTH(phone)=11

标准化 & 映射

事项说明示例
字段重命名统一命名规范(snake_case,业务语义)credit_codeunified_social_credit_code
单位统一金额→“分”或“万元”,时间→yyyy-MM-dd HH:mm:ssregistered_capital / 10000 AS registered_capital_wan
码值映射通过字典表映射标准 codedict_map('province', raw_province) AS province_code
布尔标准化统一为 0/1CASE WHEN status = 'Y' THEN 1 ELSE 0 END

维度退化(关键!)

事项说明示例
退化高频维度冗余维度表的常用描述字段,减少 JOINprovince_code+province_name
仅退化稳定维度不退化易变字段(如价格、状态)退化“行业名称”,不退化“最新评分”
退化到事实表在事实表中直接存储维度属性dwd_company_base中存industry_name

复杂类型处理(Array)

事项说明示例
多值字段用 Array来源、标签等一对多关系source_codes ARRAY<VARCHAR(10)>
存储 ID(code),非 name保证语义稳定['1', '3']而非['爬虫', '展会']
预计算常用标志基于 Array 计算布尔字段is_valid AS ARRAY_CONTAINS(tag_codes, 'valid')

表结构设计(按业务过程)

表名粒度核心字段示例说明
dwd_company_base_inc企业company_id, company_name, province_code, province_name, industry_code, ...企业基础属性 + 退化维度
dwd_contact_base_inc联系人contact_id, company_id, company_name, contact_name, position_code, ...联系人属性 + 退化企业维度
dwd_contact_channel_inc联系方式channel_id, contact_id, company_id, channel_type, channel_value, source_codes, tag_codes, is_valid, ...方式值 + Array来源/标签 + 有效性标记
dwd_patent_fact_inc专利patent_id, company_id, apply_date, grant_date, patent_type_code, patent_status_code, ...专利原子事实,不聚合

分区 & 索引

事项说明
按天分区PARTITION BY data_date
分桶DISTRIBUTED BY HASH(company_id) BUCKETS 10
倒排索引source_codes,tag_codesINVERTED INDEX

📊 三、DWS 层(Data Warehouse Summary)—— 轻度聚合层

✅ 定位:

  • 按主题、维度、时间进行轻度聚合
  • 支持多维分析、趋势统计、指标计算
  • 不存储明细,只存聚合结果

🛠️ 具体工作:

  1. 聚合计算
事项说明示例
COUNT/SUM/AVG基础聚合COUNT(*) AS patent_count
时间窗口近7/30天统计COUNT_IF(DATEDIFF(NOW(), apply_date) <= 30)
分组维度按企业、行业、地区等分组GROUP BY company_id, patent_type_code

表结构设计(按主题)

表名聚合维度核心字段示例说明
dws_company_patent_stat企业×专利company_id, total_patent_count, invention_count, last_apply_date, ...企业专利统计
dws_company_contact_stat企业×联系人company_id, total_contact_count, valid_contact_count, wechat_coverage_rate, ...联系有效性统计
dws_contact_channel_dist联系方式×来源/标签company_id, source_code, tag_code, channel_count, ...渠道分布分析
dws_company_trend_7d企业×时间company_id, stat_date, patent_apply_7d, contact_add_7d, ...近7天趋势

更新策略

事项说明
增量更新只更新有变更的企业/联系人
全量覆盖每日快照,简单可靠

🖼️ 四、DWT 层(Data Warehouse Theme)—— 主题画像层

✅ 定位:

  • 企业/联系人画像宽表
  • 只包含稳定、高频、通用的画像特征
  • 不是大宽表,而是“数据产品”

🛠️ 具体工作:

  1. 画像特征构建
事项说明示例
只选核心指标高频、稳定、跨部门通用✔️total_patent_count
昨日新增专利
特征工程基于DWS/DWD计算标签/分层CASE WHEN patent_count > 10 THEN '高潜力' ELSE '普通' END
存储ID非name维度字段仍存code,名称在查询时映射industry_code而非industry_name

表结构设计

表名主体核心字段示例说明
dwt_company_profile企业画像company_id, company_name, industry_code, total_patent_count, contact_valid_rate, customer_value_score, ...企业特征快照
dwt_contact_profile联系人画像contact_id, contact_name, position_code, is_key_person, main_channel_valid, intent_level, tag_codes, ...联系人特征快照

技术实现

事项说明
使用物化视图CREATE MATERIALIZED VIEW ...自动同步
倒排索引tag_codes建索引加速查询
分区PARTITION BY data_date,保留365天

治理规范

字段准入评审新增字段需评审:是否高频?是否稳定?
生命周期管理每季度清理低频字段
文档化下钻路径注释中说明“深度分析请查 dws_xxx”

📈 五、ADS 层(Application Data Store)—— 应用层

✅ 定位:

  • 面向报表、API、BI、模型的最终数据出口
  • 按需组合 DWT + DWD + DWS

🛠️ 具体工作:

  1. 数据服务方式
场景数据源说明
固定报表/看板DWT高性能,直接查询画像宽表
临时分析/下钻DWS + DWD灵活组合,支持复杂 JOIN
探索性分析DWD原始明细,支持任意维度下钻
API/模型输入DWTDWS按需封装,提供稳定接口

示例查询

-- 场景1:销售日报(查DWT)
SELECT company_name, total_patent_count, customer_value_score
FROM dwt_company_profile
WHERE customer_value_score > 80 AND data_date = '2025-09-22';

-- 场景2:专利类型×联系人职位分析(查DWS+DWD)
SELECT 
  dwd.position_name,
  dws.patent_type_code,
  s.patent_type_name,
  COUNT(*) as cnt
FROM dwd_contact_base dwd
JOIN dws_company_patent_by_type dws ON dwd.company_id = dws.company_id
JOIN dim_patent_type s ON dws.patent_type_code = s.patent_type_code
WHERE dws.data_date = '2025-09-22'
GROUP BY dwd.position_name, dws.patent_type_code, s.patent_type_name;

性能优化

事项说明
Doris 查询改写自动路由到物化视图
结果缓存对高频查询结果做 Redis 缓存
分页下推BI 工具查询时下推 LIMIT/OFFSET

📜 六、配套规范 & 治理

1. 命名规范

ODSods_系统名_表名ods_crm_company
DWDdwd_业务域_表名_更新模式dwd_company_base_inc
DWSdws_主题_维度_时间粒度dws_company_patent_1d
DWTdwt_主体_类型dwt_company_profile
ADSads_应用场景_表名ads_sales_dashboard

字段规范

类型命名示例
IDxxx_idcompany_id
名称xxx_namecompany_name
编码xxx_codeprovince_code
数量xxx_countpatent_count
金额xxx_amountorder_amount
比率xxx_ratecontact_valid_rate
时间xxx_time/xxx_datecreate_time,stat_date

数据质量监控

指标监控方式阈值
主键非空率COUNT(*) / COUNT(company_id)> 99.9%
枚举值合规率COUNT_IF(source_code IN ('1','2','3')) / COUNT(*)= 100%
数据波动率ABS(today_count - yesterday_count) / yesterday_count< 30%
时效性MAX(etl_time) - NOW()< 1小时

🧩 七、架构全景图

[源系统]
   ↓ 全量/增量同步
[ODS] —— 贴源、分区、保留原始结构
   ↓ 清洗、标准化、退化维度、Array存code
[DWD] —— 原子事实表(企业、联系人、专利、联系方式)
   ↓ 按主题聚合(企业×专利、企业×联系人、渠道×标签)
[DWS] —— 轻度聚合表
   ↓ 特征工程、构建画像(只存稳定高频特征)
[DWT] —— 企业/联系人画像宽表(物化视图)
   ↓ 按需组合
[ADS] —— 报表/API/BI/模型
        ├─ 固定需求 → DWT
        ├─ 临时分析 → DWS + DWD
        └─ 探索分析 → DWD

✅ 总结:你的数仓体系已具备企业级能力

通过以上设计,你的数仓将实现:

  • 清晰分层:各司其职,避免职责混乱
  • 高性能:Doris + 物化视图 + 倒排索引
  • 高灵活:DWT 做画像,DWD/DWS 做深度分析
  • 高可维护:命名规范、数据质量监控、治理机制

问题:忘了提一点,我们数仓分层没有 表分区,也就是分析的都是截至当前的存量数据,也就是dwt层表没有历史都是当前存量算的。这样就没有增加dwt维度还要算历史分区数据的问题了

太好了!你补充的这一点非常关键 —— “数仓无分区,所有分析基于当前存量数据”,这直接决定了我们整个建模体系的设计重心和优化方向。

这意味着:

DWT 层无需处理历史快照,只维护“当前最新画像”
DWD/DWS 也无需按天分区,全量覆盖更新即可
ETL 逻辑更简单,资源消耗更低,查询性能更高
“画像层”可以更轻量、更实时、更聚焦当前业务状态

🧱 更新版:无分区存量数仓建模体系(高企咨询场景)

📌 核心原则:所有表均为“当前最新全量”,无历史分区,无时间旅行

🗃️ 一、ODS 层(贴源层)—— 仍保留原始结构

✅ 职责不变:

  • 全量/增量同步源表
  • 保留逻辑删除数据
  • 添加 etl_time, source_system

🔄 更新策略:

  • 全量覆盖:每日或每小时全量抽取最新数据
  • 无分区:单表存储最新状态
-- 示例:ods_company (无分区,全量最新)
CREATE TABLE ods_company (
  company_id BIGINT,
  company_name STRING,
  is_deleted TINYINT,
  gmt_modified DATETIME,
  etl_time DATETIME,
  source_system STRING
) ENGINE=OLAP
DUPLICATE KEY(company_id); -- Doris 用 DUPLICATE 模型存明细

🧱 二、DWD 层(明细建模层)—— 当前最新原子数据

✅ 核心变化:

  • 不按天分区,只存当前最新状态
  • ETL 每次全量重建或增量合并(推荐全量重建,简单可靠)

🛠️ 具体工作(更新版):

1. 数据清洗 & 过滤

事项说明
过滤逻辑删除WHERE is_deleted = 0
去重ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY gmt_modified DESC) = 1→ 取最新一条
空值/异常处理同前,标准化处理

标准化 & 退化维度

事项说明
字段重命名、单位统一、码值映射同前
退化维度仍退化province_name,industry_name等高频维度

Array 存储(关键!)

事项说明
存储来源/标签ID(code)source_codes ARRAY<VARCHAR(10)>['1','3']
预计算标志位is_valid AS ARRAY_CONTAINS(tag_codes, 'valid')

表结构示例(无分区)

-- dwd_company_base (当前最新全量)
CREATE TABLE dwd_company_base (
  company_id BIGINT,
  company_name STRING,
  unified_social_credit_code STRING,
  province_code VARCHAR(10),
  province_name STRING,          -- 退化维度
  industry_code VARCHAR(10),
  industry_name STRING,          -- 退化维度
  registered_capital_wan DECIMAL(10,2),
  is_high_tech TINYINT,
  etl_time DATETIME
) ENGINE=OLAP
DUPLICATE KEY(company_id)
DISTRIBUTED BY HASH(company_id) BUCKETS 10;

-- dwd_contact_channel (当前最新全量)
CREATE TABLE dwd_contact_channel (
  channel_id BIGINT,
  contact_id BIGINT,
  company_id BIGINT,
  channel_type VARCHAR(20),
  channel_value STRING,
  source_codes ARRAY<VARCHAR(10)>,   -- 存ID,如 ['1','2']
  tag_codes ARRAY<VARCHAR(20)>,      -- 存ID,如 ['valid','not_company']
  is_valid TINYINT AS ARRAY_CONTAINS(tag_codes, 'valid'), -- 虚拟列或ETL计算
  is_primary TINYINT,
  etl_time DATETIME
) ENGINE=OLAP
DUPLICATE KEY(channel_id)
DISTRIBUTED BY HASH(company_id) BUCKETS 10;

💡 Doris 提示

  • DUPLICATE KEY 模型存储明细数据
  • DISTRIBUTED BY HASH 分桶提升查询性能
  • source_codes, tag_codes 建倒排索引加速查询

📊 三、DWS 层(轻度聚合层)—— 当前最新聚合

✅ 核心变化:

  • 无时间维度,只计算当前总量/状态
  • 每次全量重建,简单高效

🛠️ 具体工作:

1. 聚合计算(当前状态)

事项说明示例
总数统计COUNT(*)total_patent_count
分布统计GROUP BY patent_type_codeinvention_count,utility_count
覆盖率SUM(is_valid) / COUNT(*)contact_valid_rate

表结构示例

-- dws_company_patent_stat (当前最新)
CREATE TABLE dws_company_patent_stat (
  company_id BIGINT,
  total_patent_count BIGINT,
  invention_count BIGINT,
  software_copyright_count BIGINT,
  last_apply_date DATE,
  etl_time DATETIME
) ENGINE=OLAP
UNIQUE KEY(company_id)  -- 每个企业一条最新记录
DISTRIBUTED BY HASH(company_id) BUCKETS 10;

-- dws_company_contact_effectiveness
CREATE TABLE dws_company_contact_effectiveness (
  company_id BIGINT,
  total_contact_count BIGINT,
  valid_contact_count BIGINT,
  wechat_contact_count BIGINT,
  contact_valid_rate DECIMAL(5,4),
  etl_time DATETIME
) ENGINE=OLAP
UNIQUE KEY(company_id)
DISTRIBUTED BY HASH(company_id) BUCKETS 10;

💡 Doris 提示

  • UNIQUE KEY 模型,自动覆盖旧数据
  • 每次 ETL 全量 INSERT OVERWRITE,或用 MERGE 操作

🖼️ 四、DWT 层(主题画像层)—— 当前最新画像(无历史包袱!)

✅ 重大利好:

  • 无需考虑“新增字段要重算历史”问题 → 加字段只需跑一次当前全量!
  • 画像表结构可灵活调整,无历史兼容压力
  • ETL 超级简单:JOIN 当前 DWS + DWD → 生成最新画像

🛠️ 具体工作:

1. 画像特征构建

事项说明
只选当前核心指标total_patent_count,contact_valid_rate
特征工程基于当前数据打标签,如CASE WHEN patent_count > 10 THEN '高潜力'
存储 ID 非 name仍存industry_code, 查询时再 JOIN 名称

表结构示例(当前最新全量)

-- dwt_company_profile (当前最新画像)
CREATE TABLE dwt_company_profile (
  company_id BIGINT,
  company_name STRING,
  industry_code VARCHAR(10),
  province_code VARCHAR(10),
  total_patent_count BIGINT,
  total_copyright_count BIGINT,
  contact_valid_rate DECIMAL(5,4),
  customer_value_score DECIMAL(5,2), -- RFM 或自定义评分
  company_scale_level TINYINT,       -- 1~5 分层
  is_high_potential TINYINT,         -- 是否高潜力
  profile_update_time DATETIME,
  etl_time DATETIME
) ENGINE=OLAP
UNIQUE KEY(company_id)  -- 每个企业一条最新画像
DISTRIBUTED BY HASH(company_id) BUCKETS 10;

ETL 逻辑(超级简单!)

-- 每次全量重建最新画像
INSERT OVERWRITE dwt_company_profile
SELECT
  dwd.company_id,
  dwd.company_name,
  dwd.industry_code,
  dwd.province_code,
  dws1.total_patent_count,
  dws2.total_copyright_count,
  dws3.contact_valid_rate,
  -- 特征工程
  (dws1.total_patent_count * 0.4 + dws3.contact_valid_rate * 0.6) * 100 AS customer_value_score,
  CASE 
    WHEN dws1.total_patent_count > 10 THEN 5
    WHEN dws1.total_patent_count > 5 THEN 4
    ELSE 3
  END AS company_scale_level,
  CASE WHEN dws1.total_patent_count > 5 AND dws3.contact_valid_rate > 0.8 THEN 1 ELSE 0 END AS is_high_potential,
  NOW() AS profile_update_time,
  NOW() AS etl_time
FROM dwd_company_base dwd
LEFT JOIN dws_company_patent_stat dws1 ON dwd.company_id = dws1.company_id
LEFT JOIN dws_company_copyright_stat dws2 ON dwd.company_id = dws2.company_id
LEFT JOIN dws_company_contact_effectiveness dws3 ON dwd.company_id = dws3.company_id;

优势:

  • 新增字段?改一下 SELECT 语句,跑一次全量即可!
  • 无历史数据重算压力
  • 画像永远是最新状态,业务用得放心

📈 五、ADS 层(应用层)—— 直接查当前最新数据

✅ 查询方式:

-- 场景1:查高潜力企业(直接查DWT)
SELECT company_name, total_patent_count, customer_value_score
FROM dwt_company_profile
WHERE is_high_potential = 1
ORDER BY customer_value_score DESC;

-- 场景2:分析专利类型分布(查DWS)
SELECT 
  s.patent_type_name,
  SUM(dws.patent_count) as total_count
FROM dws_company_patent_by_type dws
JOIN dim_patent_type s ON dws.patent_type_code = s.patent_type_code
GROUP BY s.patent_type_name;

✅ 性能优化:

  • Doris 自动下推谓词、列裁剪
  • 对高频查询字段建索引
  • BI 工具直连 Doris,无需中间层

📜 六、配套规范(更新版)

1. 命名规范(去掉时间标识)

层级命名规则示例
ODSods_系统名_表名ods_crm_company
DWDdwd_业务域_表名dwd_company_base(去掉_inc
DWSdws_主题_维度dws_company_patent_stat(去掉_1d
DWTdwt_主体_类型dwt_company_profile
ADSads_应用场景ads_sales_dashboard

更新策略

层级策略说明
ODS全量覆盖每日/每小时全量同步最新
DWD全量重建每次 ETL 重建最新明细
DWS全量重建JOIN DWD 重新聚合
DWT全量重建JOIN DWS + DWD 重新生成画像

💡 为什么推荐全量重建?

  • 无历史分区,数据量可控(千万级以内)
  • 逻辑简单,不易出错
  • Doris 写入性能强,全量重建很快

🧩 七、架构全景图(无分区版)

[源系统]
   ↓ 全量同步
[ODS] —— 当前最新全量,保留原始结构
   ↓ 全量重建:清洗、标准化、退化、Array存code
[DWD] —— 当前最新原子事实(企业、联系人、专利、联系方式)
   ↓ 全量重建:按主题聚合
[DWS] —— 当前最新轻度聚合(专利统计、联系有效性)
   ↓ 全量重建:特征工程、构建画像
[DWT] —— 当前最新企业/联系人画像(无历史包袱!)
   ↓ 直接查询
[ADS] —— 报表/API/BI
        ├─ 固定需求 → DWT
        └─ 临时分析 → DWS + DWD

✅ 总结:无分区存量数仓的“三大优势”

  1. 架构更简单:无分区、无历史、无时间旅行
  2. 维护更轻松:加字段、改逻辑,只需跑一次全量
  3. 性能更优:Doris + 全量 + 倒排索引,查询飞快

你的数仓设计已经非常务实高效 —— 不是所有企业都需要“历史快照”,聚焦当前业务状态才是王道!