DD 数据开发学习站DATA ENGINEERING
模块 项目 · 项目实战

项目数据字典

8 张表的字段、关系、脏数据与核心指标标准答案,只服务项目线。

难度 · 入门 约 20 分钟 选学 练习 8 项 计划内 刷题 0/4
本页目录 · 16 节

本章目标:拿到项目里的任意一张表,能说出字段、主键、和谁关联;能把 GMV、AOV、复购率等核心指标算到标准答案。 前置知识:完成项目 00 章的数据准备,读过 SQL 基础 的前几节。 用途:只服务项目线,做项目、对口径、面试前回查时使用。理论章节不依赖这张字典。

1. 为什么需要数据字典

数据字典回答三个问题:这张表存什么、每个字段什么意思、表之间怎么关联。没有它,你看到 order_delivered_customer_date 为空时,不知道该删还是该留;看到 customer_unique_id 和 customer_id 时,不知道哪个才是用户主键。

数据开发不仅要会写 SQL,还要能维护口径。同一个“GMV”,含不含运费、算不算取消单、按订单去重还是按明细求和,结果可能差很多。口径必须写下来,代码才能复查,面试才讲得清。

2. 核心概念

2.1 八张表一览

表名 业务含义 行数 粒度 主键
orders 订单主表 99,441 一个订单 order_id
order_items 订单明细 112,650 订单 × 商品行 order_id + order_item_id
payments 支付流水 103,886 订单 × 支付笔 order_id + payment_sequential
customers 客户与收货地址 99,441 一个 customer_id customer_id
products 商品 32,951 一个商品 product_id
sellers 卖家 3,095 一个卖家 seller_id
order_reviews 订单评价 99,224 一条评价 review_id(有重复)
category_translation 品类翻译 71 一个品类 product_category_name

合计 550,759 行。数据来自 Olist 巴西电商公开数据集,时间范围 2016-09-04 ~ 2018-10-17,分析窗口取 2017-01 ~ 2018-08。

2.2 表关系与关联键

customers 1 ───< orders 1 ───< order_items >─── 1 products
                   │                │
                   │                └────────── 1 sellers
                   ├──< payments
                   └──< order_reviews

products.product_category_name ─── 1 category_translation
orders.customer_id            → customers.customer_id
order_items.order_id          → orders.order_id
order_items.product_id        → products.product_id
order_items.seller_id         → sellers.seller_id
payments.order_id             → orders.order_id
order_reviews.order_id        → orders.order_id
products.product_category_name → category_translation.product_category_name

orders 和 order_items 是一对多;payments、order_reviews 都可能一单多行,直接 JOIN 明细会 fan-out。customers.customer_unique_id 才是自然人 ID,一个自然人可能对应多个 customer_id(多个收货地址),复购必须用它。

2.3 字段清单

表 字段 类型 说明
orders order_id string 订单 ID,主键
orders customer_id string 收货地址维度 ID,关联 customers
orders order_status string delivered/shipped/canceled/unavailable 等
orders order_purchase_timestamp string 下单时间,ODS 保留字符串
orders order_approved_at string 审核时间,160 条为空
orders order_delivered_carrier_date string 发货时间,1,783 条为空
orders order_delivered_customer_date string 签收时间,2,965 条为空
orders order_estimated_delivery_date string 预计送达时间
order_items order_id string 订单 ID
order_items order_item_id int 明细序号
order_items product_id string 商品 ID
order_items seller_id string 卖家 ID
order_items shipping_limit_date string 卖家发货截止时间
order_items price decimal 商品单价
order_items freight_value decimal 运费,383 条为 0(包邮)
payments order_id string 订单 ID
payments payment_sequential int 支付序号
payments payment_type string credit_card/boleto/voucher/debit_card/not_defined
payments payment_installments int 分期数
payments payment_value decimal 支付金额
customers customer_id string 收货地址维度 ID,主键
customers customer_unique_id string 自然人 ID,复购口径用它
customers customer_zip_code_prefix string 邮编前缀
customers customer_city string 城市
customers customer_state string 州,两字母缩写
products product_id string 商品 ID,主键
products product_category_name string 葡萄牙语品类,610 条为空
products product_weight_g int 重量(克)
products product_length_cm / height_cm / width_cm int 尺寸
sellers seller_id string 卖家 ID,主键
sellers seller_zip_code_prefix string 邮编前缀
sellers seller_city string 城市
sellers seller_state string 州
order_reviews review_id string 评价 ID,789 个 ID 重复、多出 814 行
order_reviews order_id string 订单 ID
order_reviews review_score int 1–5 分
order_reviews review_comment_title / message string 评论标题和正文,大量为空
order_reviews review_creation_date string 评价创建时间
category_translation product_category_name string 葡萄牙语品类名
category_translation product_category_name_english string 英文品类名

2.4 真实脏数据

# 表 问题 数量 处理
1 order_reviews review_id 重复 789 个 ID / 多出 814 行 按 order_id 聚合均分,不直接 DISTINCT
2 order_reviews 一单多条评价 547 先聚合到订单粒度
3 orders delivered 状态但无签收时间 8 不进履约分母,单独标记
4 orders 有效订单缺签收时间 1,737 未签收,正常,不进履约分母
5 orders 发货早于下单 / 签收早于发货 166 / 23 保留,质量报告披露
6 products 品类为空 610 关联翻译表后归为 unknown
7 orders 取消/不可用订单仍有支付 1,234 告警,不计入有效 GMV
8 payments 支付与明细差额 441 单差 >0.01 这些订单净差 -1,851.52,披露不修改
9 orders 全部订单无明细 / 有效订单无明细 775 / 8 保留在订单表,后者不进 AOV 分母
10 order_reviews 评价时间早于签收 8,320 保留,不参与评分趋势

order_delivered_customer_date 为空多数不是脏数据。它表示订单还没签收,要和“delivered 状态却没有签收时间”的 8 条分开处理。

2.5 清洗口径与规模

标准清洗顺序:

  1. 订单:排除 canceled 和 unavailable,得到有效订单。
  2. 明细:只保留属于有效订单的 order_items 行。
  3. 关联:LEFT JOIN 客户、商品、品类翻译、卖家,补上可读维度。
  4. 评价:先按 order_id 聚合均分,再 JOIN 明细,避免 fan-out。
  5. 时间:字符串 CAST 成 DATE / TIMESTAMP,派生 order_date、is_late、delivery_days。
指标 数值 说明
原始订单 99,441 8 张表里的 orders
有效订单 98,207 排除 canceled 625 + unavailable 609
有效明细行 112,101 属于有效订单的明细
有明细的有效订单 98,199 8 个有效订单没有明细

2.6 核心指标标准答案

以 data/metrics.json 为唯一基线。

指标 口径 标准答案
GMV(含运费) SUM(price + freight_value),仅有效订单 15,740,216.93
GMV(不含运费) SUM(price),仅有效订单 13,494,400.74
客单价 AOV GMV(含运费)÷ 有明细的有效订单 98,199 160.29
下单买家 有效订单中 COUNT(DISTINCT customer_unique_id) 94,990
复购买家 有效订单数 ≥ 2 的买家 2,888
复购率 2,888 ÷ 94,990 3.04%
迟到率 迟到订单 ÷ 有签收和预计时间的有效订单 8.11%
评价均分 全部 99,224 条评价的 AVG(review_score) 4.09
平均 / 中位履约 下单日到签收日 12.5 / 10.0 天
分析窗口 GMV 2017-01 ~ 2018-08 15,688,396.64

品类 GMV Top5(窗口内,含运费):health_beauty 1,432,395.00、watches_gifts 1,294,824.43、bed_bath_table 1,239,779.55、sports_leisure 1,146,420.59、computers_accessories 1,049,201.49。Top10 累计占比 62.37%。

AOV 的分母容易错:不是 99,441(原始订单),不是 98,207(有效订单),也不是 112,101(明细行),而是 98,199(有明细的有效订单)。

2.7 签收时间空值

orders 有 2,965 条 order_delivered_customer_date 为空,其中大部分是未签收订单,属于正常业务空值;只有 8 条 order_status = 'delivered' 却没有签收时间,才是异常。

判断空值是否脏,看三点:业务上是否允许为空;空值是否和业务状态一致;会不会影响当前指标。计算 GMV 不需要该字段,做履约分析时只取签收和预计时间都不为空的有效订单。

3. 动手示例

先跑数仓和体检:

python project/build_warehouse.py
python scripts/check_data.py

构建完成后,用 DuckDB 查询数仓文件:

python - <<'PY'
import duckdb
con = duckdb.connect('project/warehouse.duckdb', read_only=True)
print(con.sql("""
    SELECT
        COUNT(*) AS total_orders,
        COUNT(*) FILTER (WHERE order_delivered_customer_date IS NULL) AS null_delivered,
        COUNT(*) FILTER (
            WHERE order_status = 'delivered'
              AND order_delivered_customer_date IS NULL
        ) AS delivered_missing_date
    FROM ods_orders
""").df())
PY

预期结果:

total_orders  null_delivered  delivered_missing_date
99441         2965            8

空值数量和业务状态对得上,说明缺失主要来自未签收;8 条 delivered 缺签收时间才是异常。再用 check_data.py 的输出对照 98,207 / 112,101 / 98,199 和 GMV,能对上就说明清洗口径正确。

4. 常见坑

坑 后果 处理
把签收时间为空当脏数据全删 未签收订单被删,订单数和 GMV 对不上 先看订单状态,只把 8 条 delivered 缺时间标为异常
用 customer_id 算复购 同一人多个地址被拆成多用户,复购率偏低 复购统一用 customer_unique_id
评价或支付直接 JOIN 明细 fan-out,GMV 和评分被重复计算 先聚合到订单粒度再 JOIN
AOV 分母用有效订单数 结果偏小 用有明细的有效订单 98,199
拿全部支付减有效明细对账 多出约 26.9 万,误判数据错误 先对齐口径,再比“同时有明细和支付”的订单
重新下载数据后不回看基线 标准答案可能变化 重新跑 check_data.py,对比 metrics.json

5. 在线练习

题目 考点
175. 组合两个表 LEFT JOIN
183. 从不订购的客户 反连接
1084. 销售分析 III 时间窗口聚合
1321. 餐馆营业额变化增长 移动窗口

自测:

  • 能用 LEFT JOIN 和 NOT EXISTS 各写一条“没有下过有效订单的客户”。
  • 能在固定时间窗口内按品类聚合 GMV,并说清分子和分母。
  • 能用窗口函数算 7 日移动平均,并解释为什么要先固定日期范围。

6. 面试延伸

面试题:什么是数据口径?为什么数据开发要负责口径?

是什么:口径是指标的业务定义和计算规则,包括统计对象、过滤条件、时间范围、分母和单位。

解决什么问题:同一份数据,不同口径会算出不同结果。把口径写进数据字典和 SQL 注释,报表、分析和财务才能对得上;出问题时也能定位是数据错还是定义不同。

我的实操边界:我在项目里统一定义了有效订单、GMV 含运费、AOV 分母、复购 ID 和迟到判定;没有在公司级指标平台上维护过跨部门口径。

面试题:空值和脏数据有什么区别?

是什么:空值是字段没有值;脏数据是值不符合业务规则,例如重复评价 ID、支付与明细不一致、时间倒挂。

解决什么问题:空值可能合法也可能非法,要看业务含义。未签收订单没有签收时间合法,delivered 状态却没有签收时间才异常。清洗前先分类,避免误删正常数据。

我的实操边界:我在项目里保留未签收订单的空时间,只把 8 条 delivered 缺时间标为异常;商品缺失品类归为 unknown;没有处理过线上数据质量告警。

7. 本章验收

  • 能不看文档说出 8 张表各存什么、行数大概多少。
  • 能画出表关系图,写出 7 条关联键。
  • 能说出 5 类真实脏数据的数量和处理方式,并解释签收时间空值为什么多数不算脏数据。
  • 能复述 GMV、AOV、复购率、迟到率的标准答案和各自口径。
  • 能运行本章 SQL,验证 99,441 / 2,965 / 8 和 98,207 / 112,101 / 98,199。

重新下载数据:python scripts/fetch_olist.py;重新体检:python scripts/check_data.py。下一章:速查表。

本章任务0 / 8
本页目录