——换工具之前,先换一根轴

引子:VLOOKUP 拉出的一地辅助列

月末分析会前,领导问了一句:“按客户所属行业看下收入。”

流水表里只有客户编码,行业在客户主数据表里——于是你开始拉 VLOOKUP。行业拉完,又要按区域看,再拉一列;要按科目类别看,再拉一列;要按部门看,再拉一列。半小时后,凭证明细表右边拖着七八列辅助列,文件涨到 40MB,改一个数,鼠标转圈半分钟。

停下来想想这套操作的本质:你是在把客户名称、行业、区域、科目类别,一遍一遍抄进每张凭证。

而会计这个行业,三千年前就解决了这个问题——凭证上只记科目编码,名称和层级住在科目表里。没有任何一家 ERP 会把“应收账款”四个字写进每张凭证。可我们在 Excel 里,用 VLOOKUP 干的恰恰是这件事:把维度信息抄进每笔业务。

单表思维的尽头,就是这样一地辅助列。今天要开的这扇门,不过是把你早就相信的记账原则,搬进 Excel。

一、数据模型:Excel 里藏着一台迷你数据库

Power Pivot(数据模型)内置于 Excel 2016 及以上版本,一个工作簿里藏着一台小型关系数据库:多张表住进去,表与表之间建关系,然后所有透视表共享这张关系网。

和前几篇一样,先做翻译——你会发现这张“关系网”,就是一本账的骨架:

建模概念 会计世界的原型
事实表(存业务,一堆编码加金额) 序时账、凭证明细:一行一笔业务
维度表(存对象,一行一个) 科目表、客户主数据、部门表、日历
一对多关系 一个科目挂千百笔分录
筛选沿关系传导 在总账点一个科目,明细账自动只剩它的分录
度量值 报表项目的定义:“营业收入”不是一个格子,是一个口径
筛选上下文 取数条件:“2026年6月 · 华东 · 销售费用”

看懂最后一行,这篇就懂了一半:你在报表上问的每一个问题——“6月华东的销售费用是多少”——每个词都是一个筛选条件;透视表的行标签列标题,就是在自动生成这句话。这件事你干了半辈子,只是从前用眼睛做,现在由引擎做。

二、星型模型:你早就在用,只是没见过它长这样

建模的标准结构叫星型模型:事实表居中,维度表环绕四周——一张凭证表,周围挂着科目、客户、部门、日历。

画出来看一眼:

        科目表
          |
部门表 — 凭证表 — 客户表
          |
        日历表

这就是一本账。总账统驭明细账,主数据挂接业务流水——复式记账体系天生就是星型的,六十年代数据库工程师发明的范式,账房先生用了一千年。

反过来也成立:为什么辅助列方案注定膨胀?因为那是把维度表的内容复制进事实表,每笔业务背一遍全部描述——数据冗余,文件肥大,主数据一改处处要改。正确形态从来是“编码 + 关系”,凭证如此,数据模型也如此。

三、上手半小时:从三张表到一张网

全程不写代码,五步:

  1. 每张源表 Ctrl+T 转成“表格”,起正经名字(凭证表、科目表、客户表——别用“表1”);
  2. 逐张 Power Pivot → 添加到数据模型(或从 Power Query 直接“加载到数据模型”,无缝衔接上篇 );
  3. Power Pivot 窗口 → 关系图视图:把凭证表的“科目编码”拖到科目表的“科目编码”上——连线出现,一端标"1"、一端标"*",1 的那端必须是维度表;
  4. 新建度量值(这就是 DAX 的全部入门):销售额 := SUM(凭证表[金额]);
  5. 插入数据透视表 → 来源选“此工作簿的数据模型”——此刻奇迹发生:行标签里可以直接拖科目表里的科目名称、客户表里的行业,虽然这些字段根本不在凭证表里。没有 VLOOKUP,没有辅助列,关系替你把账连上了。

以后新增数据:刷新。整张网同步更新——上篇的“过账”动作,升级成了全网过账。

顺带两个红利:模型用列式压缩存储,同样的数据文件小一大截;百万行级的表,透视起来依然轻快。

四、DAX 只有一个核心概念:上下文

很多人被 DAX 吓退,以为又是一座函数山。其实常用函数两只手数得过来:SUM、CALCULATE、DIVIDE、几个时间智能函数。真正的门槛只有一个词——上下文。

同一个度量值 销售额 := SUM(凭证表[金额]),拖到透视表里,按月看就是各月的数,按部门看就是各部门的数。公式一个字没改,数字各归各位——因为度量值不是存死的值,是活的口径:每次被引用,都在当前行列标题决定的筛选条件下重新计算一遍。

这不是玄学,是报表本来的逻辑:利润表上“营业收入”的定义是固定的,取数口径随列变。你以前用 SUMIFS 手工构造这种条件,现在行列标签自动构造——你只是第一次看见“报表项目”这个东西被真正做成了对象。

需要突破当前口径时,请出 CALCULATE——它是 DAX 里的“按指定口径重算”,味道像审计调整:

本年累计 := CALCULATE([销售额], DATESYTD(日历[日期]))
上年同期 := CALCULATE([销售额], SAMEPERIODLASTYEAR(日历[日期]))

读法就是会计语言:在“年初至本期”的口径下重算销售额;在“去年同期”的口径下重算销售额。同比、累计、占比,全靠它换口径。

配套的一个重要选择——度量值还是计算列:

计算列 度量值
何时计算 刷新时逐行算,结果存进模型 引用时才算,不占存储
类比 凭证上多印一列固定文字 报表上的一个项目定义
适用 行级属性(如单据类型标记) 一切汇总、比率、同比

默认答案写进条文:先想度量值。计算列是"抄进每行",又是我们刚批评过的那件事。

五、日期表:一张必须手工立的维度表

星型模型里有一张表,行业惯例不靠系统生成,必须自己立——日历表:从第一笔业务到最后一笔,日期连续、一天不缺,配上年、季、月、星期列。

原因很会计:财务报告的时间轴不能跟着业务走——6 月没开单,6 月就不存在了吗?报表要的正是“没有业务也有月份”的时间轴。而且 DAX 的时间智能函数(同比、累计、环比)只认连续完整的日期表,缺一天,上年同期就是空的。在 Power Pivot 里把它“标记为日期表”,时间智能即刻全线激活。

这像什么?像会计期间假设:**日历是财务的世界观,必须独立于业务而存在。**工具世界把这个前提做成了硬性要求,倒是难得的诚实。

六、避坑清单:从单表思维迁移过来的六处旧习惯

  1. 一张超级宽表。把 40 列全堆进一张表再进模型——那是把单表思维原样搬了过来。先在 Power Query 拆成事实和维度(上篇的活儿),模型只管连接;
  2. 键不干净。科目编码带空格、一边是文本一边是数字、维度表有重复编码——关系当场失效或聚合翻倍。建关系前先查重、查类型,这是数据模型里的“银行对账单调节表”;
  3. 滥用双向筛选。关系默认单向(维度筛事实),够用八成场景。图省事全开双向,筛选路径打架,数字开始“看起来差不多又对不上”——那正是最危险的状态;
  4. 滥用计算列。上一节说过了,度量值优先;
  5. 没有日期表。时间智能集体沉默,还以为函数坏了;
  6. 以为模型实时联动。源表改了数,透视表不会自己动——刷新才是过账,过账才有新余额。

结语:结构先于计算

系列走到这里,脉络合拢了:

公式解决一格,函数解决一类,Power Query 解决一个循环,数据模型解决一张网。

而这四层背后,是同一句话在不同高度的回声:条文先于公式、工序先于操作——到了模型这里,是结构先于计算。科目体系定错,账算得再准也是错账;表间结构搭错,DAX 写得再漂亮也是错数。会计人学建模,表面在学新工具,实际是把自己最熟的“总账统驭明细账、主数据挂接业务流水”第一次亲手搭出来。你以为在入门,其实在回家。

Power Query 管进料,数据模型管结构,度量值管口径,透视表管呈现——Excel 到这里,已经把一台 BI 引擎的五脏六腑给你配齐了。唯一剩下的,是把这些东西搬出桌面、给全公司看。那是下一站的事了。


系列目录:《Excel 函数的本质》 → 《LAMBDA 实战》 → 《部门函数库十讲》 → 《递归》 → 《Power Query》 → 本篇。