Community编程与开发github.com

ZrobDev/数据建模

用于设计或分析分析型模型(星型模式、数据集市、事实/维度表)、解决多表连接后的度量重复计算或膨胀问题、选择代理键与自然键、缓慢变化维度类型1或2、决定原始/暂存/中间/数据集市分层与命名规则、确定模型所需测试,以及PostgreSQL OLTP模式审查(规范化、外键索引、多租户键)。

数据建模 是什么?

该技能为开发者提供了一套系统化的分析型数据建模方法论,支持PostgreSQL、BigQuery、Snowflake等数据平台,并基于dbt进行数据转换,使用DuckDB 2.0进行本地原型验证。其核心价值在于解决数据建模中的常见痛点:粒度混淆导致的计数膨胀、多表连接后的度量重复计算、代理键与自然键的选择困境,以及缓慢变化维度的实现策略。技能强调“粒度优先”原则,要求在任何列定义前明确声明表粒度,避免将不同粒度的度量混合在同一张表中。它详细区分了事务快照、周期快照、累积快照和无事实事实表等事实类型,并指导开发者正确构建星型模式或雪花模式。此外,技能还涵盖了dbt分层架构(raw/staging/intermediate/marts)的命名规范、质量测试要求(唯一性、非空、引用完整性、新鲜度等),以及PostgreSQL OLTP模式设计的最佳实践,包括3NF规范化、外键索引、数据类型选择和多租户方案。通过遵循此技能,开发者能构建出可维护、高一致性的数据模型。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ZrobDev/data-engineering-skills/tree/HEAD/skills/data-modeling

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

数据建模 是做什么的?

目标平台:PostgreSQL, BigQuery, Snowflake。转换工具:dbt。本地原型验证:DuckDB 2.0。 有关dbt的机械操作(模型配置、测试YAML、快照、语义层、v2迁移),请使用本仓库的de-vendors技能拉取的官方dbt Labs技能。本技能决定建模什么,而非dbt如何拼写。

流程

  1. 明确业务问题及其消费者(仪表盘、机器学习特征、财务结账)。
  2. 在任何列之前,用一句话描述粒度:“一行代表每个订单行每天。”如果这句话需要包含“或”,说明应拆分为两个表。
  3. 选择事实类型(如下),然后列出在该粒度下有效的维度。
  4. 命名粒度键(自然列或它们的哈希值)。该键需设置unique + not_null约束。
  5. 在DuckDB中对样本数据原型验证,执行fan-out检查,然后移植。
  6. 在声明完成前,添加“测试什么”中列出的测试。

粒度

  • 粒度优先,列定义其次。每个度量和每个外键在粒度级别上必须是单值的;不属于该粒度的列应放在另一个表中,否则会默默地导致行数膨胀。
  • 保持源支持的最低粒度。聚合值可从明细推导,但明细不可从聚合值推导。将汇总表构建为独立的数据集市。
  • 绝不要在同一张表中混合不同粒度(例如,订单头总额重复出现在订单行行上)。对订单行求和时,头表金额会被重复计算;应将头表和行表的事实分开建模。

事实与维度

  • 事实:事件或度量;数值类型,尽可能可加,由维度键和退化维度(如订单号)关联。
  • 维度:用于过滤和分组的上下文描述。宽而反范式化是可以的。
  • 分类每个度量:可加性(在所有维度上求和)、半可加性(余额:不能跨时间求和)、非可加性(比率:存储分子和分母,聚合后相除)。
  • 事实类型:
    • 事务事实表:每个事件一行。默认选择。
    • 周期快照事实表:每个实体每个周期一行(每日余额)。当状态很重要且事件稀疏或延迟时使用。
    • 累积快照事实表:每个流程实例一行,每个里程碑有一个日期列,原地更新。用于具有已知生命周期的管道(已下单、已发货、已送达)。预期会发生更新;使加载幂等。
    • 无事实事实表:没有度量的事件或覆盖(如出勤、资格)。统计行数。

模式形状

  • 默认使用星型模式:一个事实表,扁平维度。只有当子维度被共享且较大,或由另一个团队维护时,才使用雪花模式(规范化维度);额外的连接会损害可读性和BI工具兼容性。
  • 一致性维度:一个dim_customer、dim_date被所有事实表共享,具有相同的键和属性含义。只构建一次;另一个具有不同规则的客户表是缺陷,因为跨事实报表会不一致。
  • 角色扮演维度(如order_date、ship_date)通过单独的外键列复用一个dim_date。
  • 垃圾/迷你维度用于低基数的标志位;不要在事实表上遗留8个布尔列。
  • 未知成员:在每个维度中保留一行(键为-1或'unknown'的哈希值),以便迟到的事实能保留其行。内连接会静默地删除事实行;应使用左连接并coalesce到未知键。

键

  • 自然键:源系统的业务标识符。始终保留为属性。
  • 代理键:当自然键为组合键、可能变化、来自多个源,或维度为SCD2(一个实体有多个版本)时,添加代理键。在dbt风格的仓库中,优先使用自然键(加上SCD2的valid_from)的确定性哈希值,而非序列,以确保重新运行和并行构建时一致。
  • 不要将代理键作为标识符暴露给业务用户;它们在重建时会改变。

缓慢变化维度

  • 类型1(覆盖):当历史记录无分析价值或变更属于修正时使用。
  • 类型2(每个版本新增一行,含valid_from / valid_to / is_current):当必须按事件时间报告属性值(如购买时的客户等级)时使用。
  • 当源系统不保留历史记录且你仅需捕获未来的变化时,快照足以应对;快照是捕获机制,其上的SCD2维度模型仍然存在。如果源系统已有历史记录或事件日志,则从中派生有效范围,而非进行快照。
  • 将事实表与SCD2连接:基于键加上event_ts >= valid_from and event_ts < valid_to(半开区间,避免重叠)。测试每个自然键下是否无重叠或无间隙的范围。
  • 类型3或按属性分类型的类型2:仅在单个“先前值”列能回答问题时才使用;否则跳过。

Fan-out与重复计算

原因:连接粒度不匹配的表,导致“一”方实际为“多”。症状:总计大于源数据,count(*)在左连接后增加,平均值偏移。

  • 检测:比较每次连接前后count(*)和count(distinct grain_key)的值;它们必须相等(连接维度后)。测试维度侧连接键的唯一性(select key from dim group by 1 having count(*) > 1)。
  • 核对:数据集市中度量的总和等于相同过滤条件下源数据中的总和。
  • 修复方法:在连接前,将“多”方聚合到事实的粒度,或通过带有分配权重的桥接表连接;绝不能使用distinct隐藏问题。
  • 多对多关系(客户到账户,产品到类别):使用带有权重的桥接表,或选择一个主表并说明。
  • 这些检查的SQL参见:references/fan-out-checks.md。

分层

  • raw/staging:每个源表对应一个staging模型;重命名、类型转换、去重,无连接,无业务逻辑。保持raw层不可变。
  • intermediate:连接、重塑、被2个及以上数据集市复用的共享逻辑。不向BI暴露。
  • marts:事实表和维度表,每个业务过程对应一个数据集市,已文档化、测试化、契约化。只有marts是公开的。
  • 依赖关系单向流动;marts不读取raw,staging从不读取其他staging。
  • staging物化为视图,marts物化为表(当数据量大且粒度键稳定时采用增量模式)。

命名

  • stg_<源系统>__<实体>、int_<实体>_<动词>、fct_<过程>、dim_<实体>;snake_case;统一使用单数或复数(选一种)。
  • 键:<实体>_id(自然键)和<实体>_key(代理键);时间戳:<事件>_at(UTC);日期:<事件>_date;布尔值:is_ / has_;金额携带单位或货币(amount_usd)。

测试什么

  • 粒度键:每个模型上设置unique + not_null;组合粒度测试组合的唯一性。
  • 事实表上的每个外键:not_null(或未知成员)以及指向维度的relationships测试。
  • 状态/类型列上的已接受值;新值会显式失败,而非被错误分类。
  • 每个数据集市与源数据的行数和总和核对,给出明确的容差(财务场景为零)。
  • SCD2:无重叠范围,每个自然键恰好有一行当前记录。
  • 源数据的新鲜度。在样本数据上至少破坏每个测试一次,以证明它能失败。

OLTP模式审查(PostgreSQL)

  • 事务表规范化到3NF;仅在可衡量的读路径上反范式化。每个表都有主键。
  • 为外键列建立索引:PostgreSQL不会自动创建,无索引的外键会使父表删除和连接操作扫描子表。
  • 使用timestamptz、货币使用numeric、文本使用text而非任意varchar限制;通过NOT NULL、CHECK、UNIQUE和FK约束强制执行规则,而非仅依赖应用代码。
  • 对于大量插入的表,优先使用bigint标识列或UUIDv7键,而非随机的UUIDv4(索引局部性)。
  • 多租户:在每个租户拥有的表上放置tenant_id,使其成为组合键和索引的引导列,包含在外键中(组合外键)以消除跨租户引用,并配合行级安全策略。默认使用共享schema加tenant_id;仅当需要硬隔离或按租户恢复时才使用每个租户独立schema或数据库。
  • 软删除:使用部分唯一索引(where deleted_at is null)以保持唯一性约束。

陷阱和已观测故障模式:references/gotchas.md

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

相关技能