继续介绍一下数据仓库的四个特点:面向主题、集成、时变、非易失。数据仓库把分散的业务数据整合到一起,供分析师分析业务、并为决策提供数据支持。它按要研究的业务范围安排数据,让不同来源的数据含义一致,并留下能说明过去情况的记录。

一、从订单分析看:数据仓库为什么需要这四个特点

订单系统首先要处理下单、支付、取消、发货、收货等业务动作。客服查看一笔订单时,通常希望看到它现在的状态。分析人员的问题则可能是:上月每个地区收到了多少支付款,发生了多少退款,客户迁居后应怎样解释以前的消费?

这些问题跨越订单、支付、退款和客户系统,还涉及不同时间。如果只是把表复制到另一台机器,可以让查询不直接读业务库,但复制本身不会统一客户身份、金额单位和历史地区,也不会替我们决定退款归属哪个分区。

特点 核心含义 订单分析中的落点 常见误解
面向主题 按需要研究的业务对象和活动组织数据 围绕交易、客户、商品安排分析数据 按来源数据库名归档就算主题
集成 数据整合,不同来源的字段进行归一、处理 同一客户、同一币种、明确金额口径 把表集中到一个库就完成了整合
时变 保留时间上的变化及其历史背景 能区分交易时间与客户地区有效期 每晚更新一次就是时变
非易失 已记录的历史应保持稳定,避免随意覆盖 退款另记事件,纠错保留依据 仓库永远不能更新或删除

下面沿着同一项订单分析,逐一看这些特点如何落地。

二、面向主题:先确定业务问题,再组织数据

面向主题,就是把研究同一业务范围需要的数据组织在一起,例如研究客户、销售、商品或库存。主题代表企业持续关心的业务范围,不只是一张报表,也不必属于一个部门。

销售人员关心商品卖得怎样,财务人员关心支付与退款,客服人员关心订单履约。他们可以从不同角度使用交易主题中的数据;同样,一个交易主题的数据也可能来自多个系统。来源系统说明数据在哪里产生,主题说明这些数据放在一起要研究什么。

从主题到业务过程再到每行数据

交易涉及很多活动,建表时要先说明保存的是下单、支付还是退款。这些活动叫业务过程。还要说明每行代表什么,这叫粒度。表里要衡量的结果,如支付金额,叫事实;用于说明、筛选或分组的信息,如客户、日期和地区,叫维度。

订单明细表可以每行代表一笔订单中的一种商品,支付表可以每行代表一次成功支付,退款表可以每行代表一次退款。这些粒度不同。若把订单总额复制到每个商品行,再按商品行求和,就可能重复计算;主题划分不能代替粒度设计。

要按地区看收款和退款,可以把支付和退款分别保存,再按相同的客户和日期含义分析。这些保存业务结果的表叫事实表。交易主题可以有多张相关事实表,不必把全部数据拼成一张包含大量字段的宽表。

Mermaid 图 1

这张图表达的是分析关系。支付和退款各自保留业务含义,客户属性提供分组背景;把它们归到交易主题后,分析问题可以贯通不同系统。

面向主题不是按来源系统组织数据

来源系统拆库、换字段名或迁移平台时,分析人员还是要研究交易和客户。主题让分析结构跟着业务含义走。采集和集成需要适应来源变化,分析表则要看业务含义与粒度有没有变化,再决定是否调整。

三、集成:放在一起之后,还要说同一种语言

集成就是让不同地方来的数据,使用同一套规则。同一客户的编号要能对应起来,金额单位要一致,状态也要表达同一件事。同一对象和同一种度量满足这些条件,才能一起分析;只把字段改成同名还不够。

统一身份、单位与业务定义

网站用会员编号,门店用会员卡号来认客户。要统计一个客户在不同渠道的消费,得先有可靠的对应关系,把各系统中属于这个客户的编号对到同一个客户标识上,这个过程叫身份映射。两个系统中的相同号码也可能属于不同人,不能直接合并。没有足够依据时,就保留身份尚未确认的状态。

来源 支付记录 原客户标识 金额字段与单位 统一后客户 人民币支付金额
网站 P01 web_A 10000 分 C01 100 元
门店 P02 card_B 80 元 C01 80 元

这两笔支付经过身份映射和单位转换后,才能得到客户 C01 的支付金额合计 180 元。这里统一的是人民币金额;如果来源包含其他币种,还需要决定换算币种、汇率日期及规则,不能把不同币种的数值直接相加。

状态同样需要解释。某系统的完成可能表示付款成功,另一个系统的完成可能表示已签收。它们不能机械映射为同一个支付成功状态。支付金额、下单金额和退款金额也应分别命名,不能因为都属于金额就混用。

一致性维度与关联放大

支付表和退款表如果按同一套规则识别客户、解析地区,就能各自求出地区金额,再把相同地区的结果放在一起比较。这样的共同分组信息叫一致性维度。每张表都放一个客户编号还不够,必须确认这些编号代表的身份一致。

支付和退款各自可能有多条记录,直接在订单编号上关联再求和容易放大金额。比如一笔订单有两笔支付和两笔退款,关联可能得到多种支付与退款组合。应先弄清关系与粒度,再决定分开汇总后对齐,还是建立有业务依据的分摊关系。

Mermaid 图 2

集成后的数据仍需要质量检查:身份映射是否唯一、金额转换是否正确、未知客户是否被误合并。只靠一套统一规则还不够,还要配套校验机制,两者一起才能保证数据保持一致,把各个系统的订单数据放到一起,只是第一步。订单状态怎么对应、销售额怎么算,都要先统一规则,再检查数据有没有重复、遗漏,金额能不能对上。

四、时变:保留过去发生的事,也保留当时的背景

时变,关注的是业务情况如何随时间变化。比如,分析订单时,除了看现在的状态,还可能要知道它什么时候从“待付款”变成“已付款”,以及变化发生时对应的客户、商品和时间信息。只保留最新状态,就很难还原过去发生了什么。

历史保留多久、数据多久更新一次,是两个不同的问题。每天更新客户表,并不意味着要用新资料覆盖旧资料;如果客户搬家后只留下新地址,就无法知道他过去住在哪里。留下有用的历史,才能比较不同时期的业务情况、观察变化趋势。至于写入后的数据是否应随意修改,则是下一节“非易失”要讨论的重点。

业务发生时间与入仓时间

何时付款、何时退款,是业务发生时间。记录何时进入仓库,是入仓时间。订单月底付款,记录次月才到仓库;按付款时间统计和按入仓时间统计,就会落在不同期间。仓库要保存分析需要的时间,并说明指标到底用哪个时间来分组。

源系统里的更新时间也不能直接代替业务发生时间。订单可能因为补充备注而更新,但支付并没有重新发生。若按更新时间累计支付,重新修改的订单可能落入错误月份。

历史事实之外,还要保存会影响分析的属性。客户迁居、商品分类调整,都可能改变分组结果;只留订单日期,无法恢复这些历史背景。

用客户地区变化解释历史版本

客户 C01 在 2025-01-01 至 2025-02-01 之前属于杭州,从 2025-02-01 起属于上海。支付 P01 在 2025-01-20 发生,金额为 100 元;支付 P02 在 2025-02-10 发生,金额为 80 元。

如果只保留客户的最新地区,那么两笔支付都会归到上海。若问题是支付发生时各地区的支付金额,这个结果就不符合口径。要保留客户的地区版本,并让支付关联到发生时有效的那一版。

客户版本键 客户标识 地区 生效时间 失效时间 当前版本
101 C01 杭州 2025-01-01 2025-02-01 否
102 C01 上海 2025-02-01 9999-12-31 是

同一个客户可能先后有多个地区版本,需要用客户版本键区分每一版。地区变了就增加一行,同时记下这行从什么时候有效、到什么时候失效,以及是否为当前版本。这样的历史保存方式叫缓慢变化维度的 Type 2。末行用远期日期作为失效时间,表示这一版还没有结束。

本例有效区间采用左闭右开:达到生效时间时纳入该版本,达到失效时间时转入下一版。客户的版本区间不能重叠,否则一笔支付可能关联多行;也不能在需要分析的时间范围内无故留空,否则可能找不到对应地区。

Mermaid 图 3

这个版本设计服务于按当时地区分析。按客户当前地区分析历史消费,也是另一种有意义的问题;需要明确选用当前属性,不能与按当时地区混成同一指标。是否保留某个属性的全部变化,应由分析需求决定,不要求所有字段一律历史化。

快照与历史事件的区别

要知道每天结束时有多少订单还没完成,可以把当时状态留下来,这叫每日状态快照。要知道每一次支付和退款,就保留每次发生的事件记录。快照告诉人某个时点是什么状态,事件记录告诉人发生了哪些事。一天只留最新状态,不一定能找回这一天里的全部变化。

保留历史也有存储和维护成本。有效期、迟到数据、属性版本和保留周期都需要管理;不是加一个日期字段就获得完整历史分析能力。

五、非易失:历史数据不能随意改,发现错误仍然可以修正

非易失要求仓库稳定保留已记录的历史。业务系统会因日常操作不断更新当前状态,而用户分析时则需要知道过去发生了什么,仓库应避免频繁用最新状态覆盖旧记录,才能让这些历史事实继续用于分析。

新增支付应增加相应记录,新增退款应记录退款事件。不能因为订单后来退款,就悄悄把原支付事件从历史中抹掉。支付发生过和后来发生退款,是两个需要分别解释的事实。

退款改变余额,不抹掉支付历史

沿用前面的订单,退款 R01 在 2025-02-15 发生,金额为 20 元,对应支付 P01。支付 P01 的 100 元仍然保留,退款另记 20 元。两者合计后,该笔订单截至退款发生后的收款净额为 80 元。

这些数字能被解释,是因为支付时间、退款时间和原始金额都还在。仅保留订单当前的一个净金额,无法直接回答每个月分别收了多少、退了多少。

为什么非易失不等于禁止修改

仓库仍要接收新数据,修正采集错误,补入晚到的记录。维护历史属性时,也可能把旧版本的有效期结束。跟踪订单履约的表会随着流程推进更新关键节点,这种记录同一流程各阶段进展的表叫累积快照。所以,不能把非易失理解为每张表都永远只能增加新行。

发生了新的业务、发现原记录有错、随意改掉过去,是不同的情况。纠错时可以记下为什么改、在哪一批处理,以及必要的旧值。要重新得到已发布报表的结果,还需要保存当时报表使用的数据范围和版本。非易失不会替系统自动保存这些修改依据和记录,也就是审计信息。

仓库接收数据时也要防止重复计算。同一笔支付收到多次,不能每次都当作新支付加钱。可以用稳定的业务事件标识认出它们属于同一事件,再根据依据处理版本。如果原记录需要合法更正,还得知道改的是哪条记录、影响哪些范围;只删除重复行不能替代这些判断。

Mermaid 图 4

这里的稳定性不是查询结果永远相同。新增退款、补到数据或经过批准的纠错,都可能使某个统计发生变化;系统应能说明变化来自什么,而不是让旧数据无依据地消失。

六、把四个特点落到同一份查询结果

现在把案例的目标收窄为:按支付发生时的客户地区,汇总历史支付金额。主题是交易,业务过程是成功支付,每行支付记录对应一次支付;金额已统一为人民币元,客户地区则按照有效时间匹配。

下面的查询把支付记录和客户地区版本写在同一个 SQL 中,便于看到每个条件的作用。日期字段表示日级业务时间,使用固定格式的日期文本;这个写法的日期比较建立在相同格式和日级精度上。

WITH customer_history (
    customer_key, customer_id, region,
    valid_from, valid_to, is_current
) AS (
    VALUES
        (101, 'C01', '杭州', '2025-01-01', '2025-02-01', 0),
        (102, 'C01', '上海', '2025-02-01', '9999-12-31', 1)
),
payment (payment_id, customer_id, paid_date, amount_yuan) AS (
    VALUES
        ('P01', 'C01', '2025-01-20', 100.00),
        ('P02', 'C01', '2025-02-10', 80.00)
)
SELECT
    c.region,
    SUM(p.amount_yuan) AS payment_amount_yuan
FROM payment AS p
JOIN customer_history AS c
    ON p.customer_id = c.customer_id
   AND p.paid_date >= c.valid_from
   AND p.paid_date < c.valid_to
GROUP BY c.region
ORDER BY c.region;

结果中,杭州为 100 元,上海为 80 元。客户标识把事实与属性联系起来,有效时间使每笔支付匹配当时的地区。这里没有筛选当前版本;如果只筛选当前版本再按客户标识关联,两笔历史支付就会全部归到上海。

每笔支付发生那天,必须恰好对应一个客户地区版本。代码用内关联连接两张表:只有两边条件都匹配,记录才会进入结果。找不到有效版本的支付不会进入结果;区间重叠又可能让支付匹配多次。因此 SQL 写法、版本数据质量和口径都影响金额是否正确。

分别记录收款与退款的时间

观察每个月的净收款,需要分别按支付日期、退款日期汇总。把退款作为负向金额与支付合并时,也要保留事件类型和各自的业务时间。

WITH cash_event (event_id, event_type, event_date, amount_yuan) AS (
    VALUES
        ('P01', 'payment', '2025-01-20', 100.00),
        ('P02', 'payment', '2025-02-10', 80.00),
        ('R01', 'refund', '2025-02-15', -20.00)
)
SELECT
    SUBSTR(event_date, 1, 7) AS event_month,
    SUM(amount_yuan) AS net_cash_yuan
FROM cash_event
GROUP BY SUBSTR(event_date, 1, 7)
ORDER BY event_month;

结果中,2025-01 为 100 元,2025-02 为 60 元。这是按收退款发生月计算的净收款,没有扣除其他费用,也不是会计收入。它没有修改原支付,把退款作为后续事件解释清楚。若要按商品计算订单收入,还必须另行明确商品粒度、退款分摊和会计口径。

这份查询同时依赖四个特点:交易主题给出研究范围,集成提供统一客户与金额含义,时变提供客户的历史地区,非易失使原事件能够继续被保留。缺少其中任何一项,查询仍可能返回结果,但结果回答的未必是原来的业务问题。

七、常见问题

把所有业务表同步进一个库,就有数仓了吗?

同步提供了数据入口。是否形成适合分析的仓库,还要看它有没有围绕业务主题组织数据、统一维度并保留必要历史。数据量大和查询机器独立,都不能单独证明这些要求已经满足。

时变是不是数据每天都在变化?

不是,根据业务而定。重点是能够分析不同时期的业务情况,并解释当时背景。

非易失是不是不能处理错误数据?

发现错误需要纠正,但应明确更正依据及影响范围,必要时保留旧版本和修订记录。把错误永久保存并继续用于分析,不会让历史更可信。未经解释地覆盖历史,同样会损害可信度。

每个字段都需要保留全部历史吗?

需要保留的是分析所依赖的历史。客户地区若影响历史分组,就要选择能回答历史状态的处理方式;某些只需要最新值的描述属性,可以采用不同策略。

四个特点是否要求固定分层和星型模型?

四个特点说明分析数据应该具备什么性质,没有规定一份固定的系统架构。维度模型用业务过程和维度信息来组织数据,也就是前面讲过的事实与维度;分层把不同处理工作放在不同层。一套层名或一个星型图,还不能证明数据含义已经统一、历史已经可靠。