首页/数据/数仓建模速查/反模式:事实表无边界堆列
建模基础69 条

反模式:事实表无边界堆列

把各种场景字段都塞进一张事实表(上百列),导致稀疏、难维护、口径混乱;应按业务过程拆表,密度优先。

数仓建模速查数据更新于 2026-10-13

无边界堆列是什么

「大宽事实表」反模式指把下单、支付、退款、售后等不同业务过程的字段全塞进一张事实表,列数上百、每行大量空值。后果是三重的:表稀疏导致存储与扫描成本高,字段归属混乱导致维护困难,不同过程粒度不一致导致聚合重复计数。正确做法是按业务过程拆表,每张事实表只放本过程的度量与维度键,密度优先、口径单一。

基本信息

典型症状列数持续膨胀、行内空值过半
根因把主题当过程,追求一张表查全部
治理方向按业务过程拆表 + 共享一致性维度
硬判断线一张表出现两种粒度就必须拆

典型使用场景

  • 业务方要求「一张表查所有交易指标」时,用拆表加汇总层满足,而不是无止境堆列
  • 新场景接入:先判断它属于哪个业务过程,同过程加列,不同过程开新表
  • 老宽表治理:把低频场景字段迁到独立事实表,原表只留核心过程
  • 性能治理:宽表扫描列多、压缩率低,拆表后单表扫描量显著下降
  • 口径治理:同一列在不同场景有两种含义时,拆分后各自命名并登记口径

拆分信号自查

拆分信号自查
-- 1) 列数:宽表通常几百列,正常事实表多为几十列以内
SELECT count(1) AS col_cnt FROM information_schema.columns WHERE table_name='fact_all';
-- 2) 稀疏度:统计关键列的空值率,超过一半说明字段覆盖过程不一致
SELECT
  sum(CASE WHEN pay_amt IS NULL THEN 1 ELSE 0 END) / count(1) AS pay_null_rate,
  sum(CASE WHEN refund_amt IS NULL THEN 1 ELSE 0 END) / count(1) AS refund_null_rate
FROM fact_all;  -- 空值率高的场景应拆到独立事实表

拆分信号与处理

信号判断依据处理方式
列数持续增长每季度都有新场景加列按业务过程拆表
行内空值占比过半字段覆盖的过程不一致低频场景字段移出
同一表两种粒度订单行与支付行混存拆表并各自声明粒度
同列多种语义不同场景含义不同拆列改名并分别登记口径
拆分依据是业务过程与粒度,不是主题名词。「交易」是主题,「下单」「支付」「退款」是业务过程,一张事实表对应一个过程。

易错点

  • 嫌拆表麻烦全部合并成一张:查询处处要加过滤条件,漏一次就出一份错数
  • 拆表但不建一致性维度:跨表汇总时用户、商品口径对不上,依然算不准
  • 混合粒度的字段挤在同一张表:上卷聚合时金额被重复计算
  • 宽表上大量稀疏列:存储与扫描成本同时升高,压缩效果变差
  • 为省 join 把维度属性也冗余进事实表:维表更新时事实表全量重刷

数据来源与参考

  • 维度建模反模式:大宽事实表
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌