1. 这不是简单的“分组求和”——多维聚合中的数据变形本质
你手头有一张销售表,字段包括地区、产品线、季度、销售额、成本、客户等级。现在老板问:“华东区A类产品在Q2的高净值客户平均毛利率是多少?再按城市细分看看TOP3。”——这已经不是Excel里点几下“数据透视表”就能搞定的事了。它背后是一整套多维空间下的数据切片、钻取、折叠与重投影逻辑。Part 20讲的“Data Manipulation in Multi-Dimensional Aggregation”,说白了,就是教你在高维数据立方体(Cube)里做精准外科手术:不光要切得准,还要把切下来的组织重新拼成能看懂的形状,同时保证每条血管(数据血缘)、每个细胞(原子值)都可追溯、无损、可复现。
我带过6个BI交付项目,90%以上的返工不是因为SQL写错,而是因为对“多维聚合中数据操纵”的底层机制理解偏差。比如有人用GROUP BY region, product_line, quarter后直接AVG(margin),却没意识到:当某城市某季度只有一笔订单时,这个“平均值”在业务上根本无意义;又比如用SUM(sales)/SUM(quantity)算均价,和AVG(unit_price)结果不同,但很多人根本说不清哪个才是财务口径认可的“加权均价”。这些都不是语法问题,而是维度语义、聚合粒度、空值传播、零值处理、指标可加性等一连串专业判断的叠加。本文不讲Pandas或DAX函数怎么写,而是带你回到数据建模的第一性原理:当你在写ROLLUP、CUBE、GROUPING SETS,或者配置Power BI的层次结构、Tableau的集计算时,你的操作正在数据空间里引发怎样的拓扑变形?哪些操作是保序的,哪些会丢失维度上下文?为什么一个UNION ALL比FULL OUTER JOIN更适合做跨时间周期的同比合并?这些答案,全藏在Part 20的实操肌理里。
核心关键词“Multi-Dimensional Aggregation”不是指“多个维度一起分组”,而是指维度本身具有层级结构、成员间存在继承关系、聚合过程需遵循维度完整性约束。比如“日期”维度不能只按年分组就完事,必须考虑年→季度→月→日的树状依赖;“产品”维度若存在“品类→子类→SKU”三级,那么按“品类”聚合时,所有子类的属性(如保质期、供应商)必须能向上归约,否则就会出现“A品类平均保质期=18个月,但其下所有子类保质期都是24个月”的逻辑悖论。本文将全程以真实零售分析场景为锚点,从一张原始交易明细表出发,一步步推演如何安全、可控、可审计地完成从原子记录到战略仪表盘的全链路变形。适合三类人:刚转行的数据分析师(避开教科书陷阱)、卡在报表性能瓶颈的BI工程师(找到优化支点)、需要向业务方解释“为什么这个数和你们Excel不一样”的数据产品经理(掌握话语权)。
2. 多维聚合的数据操纵不是“写SQL”,而是构建可验证的数据契约
2.1 为什么传统GROUP BY在多维场景下天然失效?
先看一个经典反例。假设你有如下销售事实表:
| order_id | region | city | product_category | product_subclass | quarter | sales_amt | cost_amt |
|---|---|---|---|---|---|---|---|
| 1001 | 华东 | 上海 | 家电 | 空调 | Q2 | 5800 | 4200 |
| 1002 | 华东 | 杭州 | 家电 | 空调 | Q2 | 4900 | 3600 |
| 1003 | 华北 | 北京 | 家电 | 冰箱 | Q2 | 3200 | 2400 |
业务需求:计算“各区域各季度的毛利率”,并支持下钻到城市级。直觉写法:
SELECT region, quarter, AVG((sales_amt - cost_amt) / sales_amt) AS avg_gross_margin FROM sales_fact GROUP BY region, quarter;问题在哪?表面看没问题,但实际执行时,数据库会先计算每行的毛利率(如order_id=1001:(5800-4200)/5800≈27.59%),再对所有行取平均。这叫行级平均,但业务真正要的是金额加权平均:总毛利/总销售额。正确解法必须先聚合分子分母:
SELECT region, quarter, SUM(sales_amt - cost_amt) / SUM(sales_amt) AS gross_margin FROM sales_fact GROUP BY region, quarter;这个区别不是精度问题,而是语义鸿沟。前者回答“平均每笔订单赚多少比例”,后者回答“该区域该季度整体赚了多少比例”。在多维聚合中,90%的指标错误源于混淆了“先计算后聚合”和“先聚合后计算”。而维度越多,这种混淆越隐蔽。比如加入product_category后,若想看“华东区空调类Q2毛利率”,用第一种写法会因杭州订单毛利率更高(4900-3600)/4900≈26.53% > 上海27.59%,导致加总失真——但业务根本不会关心单笔订单,他们只认资金池。
提示:所有涉及比率、百分比、平均值的指标,在多维聚合前必须明确其原子粒度(是按订单?按SKU?按客户?)和聚合规则(是SUM/AVG/MIN/MAX?是否加权?)。没有定义清楚这两点,任何GROUP BY都是空中楼阁。
2.2 维度完整性:被忽视的“数据契约”基石
多维聚合真正的难点不在技术实现,而在维度建模的契约精神。我们常把“地区”当成一个普通字段,但它其实是一个维度表,包含层级关系:
| region_id | region_name | parent_region_id | level | is_leaf |
|---|---|---|---|---|
| 1 | 中国 | NULL | 0 | FALSE |
| 2 | 华东 | 1 | 1 | FALSE |
| 3 | 上海 | 2 | 2 | TRUE |
| 4 | 杭州 | 2 | 2 | TRUE |
当事实表中region字段填的是“华东”,它隐含承诺:该记录属于华东所有下级城市(上海、杭州、南京等)的并集。但如果某天运营同事手动在事实表里补了一条region='华东'但city='深圳'的记录(深圳实际属华南),整个聚合结果就崩了——因为维度表里“华东”根本不包含深圳。这种错误不会报错,但会导致华东区销售额虚高、华南区漏计。
我在某快消项目踩过这个坑:市场部要求按“渠道类型”(线上/线下/分销)聚合,但ERP系统导出的订单里,“分销”渠道的channel_id字段为空,开发直接填了默认值'OTHER'。结果季度复盘时发现“OTHER”渠道销售额占总量37%,远超预期。查根源才发现:所有空值被强制归入OTHER,而真实缺失的渠道信息本应标记为UNKNOWN并单独统计。这就是典型的维度完整性破坏:用技术手段掩盖业务定义缺失。
因此,Part 20强调的“Data Manipulation”,首要任务是建立维度契约:
- 所有维度字段必须有明确定义的取值范围(枚举值 or 维度表主键)
- 层级维度必须保证路径唯一(一个城市只能属于一个省份)
- 空值必须有业务语义(是“未知”、“不适用”还是“未填写”?)
- 事实表外键必须与维度表主键严格匹配(禁止用字符串模糊关联)
没有这套契约,后续所有聚合都是沙上筑塔。这也是为什么现代BI工具(如Looker、ThoughtSpot)强制要求先定义Explore(探索模型),再写查询——它把数据契约前置化了。
2.3 聚合粒度陷阱:你以为的“汇总”,其实是“降维自杀”
多维聚合最危险的操作,是未经思考的GROUP BY导致的不可逆信息丢失。看这个例子:你想分析“各城市各季度的销售趋势”,但原始事实表只有order_date(精确到日)。如果直接:
SELECT city, YEAR(order_date) AS year, QUARTER(order_date) AS quarter, SUM(sales_amt) AS total_sales FROM sales_fact GROUP BY city, YEAR(order_date), QUARTER(order_date);表面看满足需求,但问题在于:YEAR()和QUARTER()是标量函数,它们把order_date这个连续维度离散化切割,且切割后无法还原。比如你发现上海Q2销售额异常高,想下钻看是哪几天爆发的,但原始日期信息已在GROUP BY中被丢弃——你只剩下一个数字,没有时间序列。
正确做法是保留原始日期字段,用时间维度表关联:
SELECT d.city, t.year, t.quarter, SUM(f.sales_amt) AS total_sales FROM sales_fact f JOIN dim_date t ON f.order_date = t.date_key JOIN dim_location d ON f.location_id = d.location_id GROUP BY d.city, t.year, t.quarter;这里t.quarter来自预构建的时间维度表,它不仅包含季度编号,还包含quarter_start_date、quarter_end_date、is_holiday_season等丰富属性。更重要的是,维度表与事实表通过date_key(如20240401)关联,保证了粒度对齐:事实表最小粒度是“日”,维度表也按“日”展开,聚合时不会丢失时间上下文。
注意:多维聚合中,永远优先使用预建维度表而非运行时函数。函数计算不可缓存、不可复用、不可索引,而维度表可物化、可压缩、可添加业务标签。我在某金融项目将时间维度表从函数改为物化表后,T+1报表生成时间从47分钟降至6分钟——因为数据库能直接走索引定位季度范围,不用每天重新解析365次日期。
3. 实操四步法:从原始明细到可信多维报表的完整链路
3.1 第一步:清洗与标准化——让数据先学会“说同一种语言”
多维聚合失败的首要原因,从来不是SQL写得不够炫,而是输入数据太“野”。我接手过一个电商数据集,product_category字段有27种写法:“手机”、“智能手机”、“Mobile Phone”、“Handset”、“Cellphone”……更绝的是还有“苹果手机”、“华为手机”这种带品牌名的。如果直接按此分组,你会得到27个“手机”类目,而不是1个。
标准化不是简单去重,而是建立业务术语映射字典。以产品类目为例,我的标准流程是:
- 探查分布:用
SELECT product_category, COUNT(*) FROM sales GROUP BY product_category ORDER BY COUNT(*) DESC LIMIT 20;找出高频歧义词; - 人工校验:抽样检查每种写法对应的真实商品(如“Handset”是否真指手机,还是包含对讲机?);
- 构建映射表:创建
dim_product_category_map表,字段为raw_value(原始值)、standard_value(标准值)、confidence_score(置信度,0.8=人工确认,0.6=规则匹配); - 柔性替换:在ETL中用
LEFT JOIN dim_product_category_map,优先取confidence_score>0.7的映射,其余标为UNKNOWN并告警。
关键技巧:永远保留原始字段。不要覆盖product_category,而是新增product_category_std。这样当业务质疑“为什么XX订单没进手机类”时,你能立刻查原始值,而不是陷入“是不是我映射错了”的自我怀疑。
另一个高频陷阱是数值型字段的单位混用。某物流项目中,weight字段有的单位是kg,有的是g,有的甚至是“箱”(一箱12kg)。直接SUM(weight)会得出荒谬结果。解决方案是强制单位声明:在维度表dim_unit中定义unit_id、unit_name(kg/g/box)、conversion_factor_to_base(1/0.001/12),事实表必须带weight_unit_id外键。聚合时统一换算:
SELECT SUM(f.weight * u.conversion_factor_to_base) AS total_weight_kg FROM fact_shipment f JOIN dim_unit u ON f.weight_unit_id = u.unit_id;这看似多一步,但换来的是指标可比性——华北仓和华南仓的重量数据终于能放在一起看了。
3.2 第二步:维度建模——用星型模型给数据装上“导航系统”
多维聚合的物理载体是星型模型(Star Schema):一个事实表居中,多个维度表环绕。但很多团队把“建模”等同于“画ER图”,结果建出来的是“雪花模型”(维度表再连维度表)甚至“蜘蛛网模型”(维度表互相引用)。这直接导致查询性能雪崩。
我的黄金法则:所有维度表必须直接连接事实表,禁止维度表之间关联。例如“客户”维度,不要拆成dim_customer(基础信息)+dim_customer_segment(分群标签),而应合并为dim_customer,包含segment_type、segment_score等宽表字段。理由很实在:每次JOIN维度表,数据库就要做一次哈希匹配,100万客户×10万订单=100亿次匹配,而宽表只需一次扫描。
具体到Part 20的实操,我们构建以下核心维度:
dim_date:日期维度,包含date_key(INT, 20240401),year,quarter,month,week_of_year,is_weekend,is_holiday等62个属性(别嫌多,促销分析时全用得上);dim_location:地理位置维度,location_id,city,province,region,country,is_first_tier_city(一线城);dim_product:产品维度,product_id,sku_code,category,subclass,brand,is_new_launch(新品);dim_customer:客户维度,customer_id,level(VIP/普通),acquisition_channel(来源渠道),lifecycle_stage(新客/活跃/流失)。
事实表fact_sales只保留原子事实:sale_id,date_key,location_id,product_id,customer_id,sales_amt,cost_amt,quantity。注意:绝不出现region_name、quarter_name等冗余字段——这些必须从维度表获取,确保单一数据源。
实操心得:维度表的主键设计有讲究。dim_date.date_key用INT而非DATE类型,因为INT比较快10倍以上;dim_location.location_id用自增整数而非城市拼音(避免“Shanghai”和“ShangHai”不一致);所有维度表必须有effective_date和end_date字段,支持缓慢变化维度(SCD)Type 2——这是处理客户地址变更、产品类目调整的唯一可靠方案。
3.3 第三步:聚合计算——在维度空间里做“可控坍缩”
现在进入Part 20的核心:如何在星型模型上安全聚合。记住,这不是写SQL,而是在维度坐标系中定义数据坍缩路径。
场景1:常规多维分组(GROUPING SETS)
需求:既要“各区域各季度”汇总,也要“各区域总计”、“各季度总计”、“全量总计”。传统写法要4个UNION ALL,既慢又难维护。用GROUPING SETS一行解决:
SELECT COALESCE(region, 'ALL_REGIONS') AS region, COALESCE(quarter, 'ALL_QUARTERS') AS quarter, SUM(sales_amt) AS total_sales, GROUPING(region) AS region_is_total, GROUPING(quarter) AS quarter_is_total FROM fact_sales f JOIN dim_location d ON f.location_id = d.location_id JOIN dim_date t ON f.date_key = t.date_key GROUP BY GROUPING SETS ( (region, quarter), -- 各区域各季度 (region), -- 各区域总计 (quarter), -- 各季度总计 () -- 全量总计 );GROUPING()函数返回1表示该维度被“坍缩”(即取了总计),返回0表示正常分组。这比硬编码'ALL_REGIONS'更可靠,因为当region字段本身就有'ALL_REGIONS'值时,COALESCE会误判。
场景2:滚动聚合(窗口函数)
需求:计算“华东区近3个月销售额移动平均”。关键在PARTITION BY和ORDER BY的组合:
SELECT region, year, month, sales_amt, AVG(sales_amt) OVER ( PARTITION BY region ORDER BY year, month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS moving_avg_3m FROM monthly_sales_summary;注意:ORDER BY year, month必须严格按时间顺序,否则窗口会乱。我曾见同事用ORDER BY month(忽略年份),导致12月和1月被连在一起计算——这是典型的维度顺序错误。
场景3:条件聚合(CASE WHEN + AGG)
需求:区分“新客首单”和“老客复购”的毛利率。不能写两个WHERE,要用条件聚合:
SELECT region, AVG(CASE WHEN customer_lifecycle = 'NEW' THEN (sales_amt-cost_amt)/sales_amt END) AS new_customer_margin, AVG(CASE WHEN customer_lifecycle != 'NEW' THEN (sales_amt-cost_amt)/sales_amt END) AS repeat_customer_margin FROM fact_sales f JOIN dim_customer c ON f.customer_id = c.customer_id GROUP BY region;这里AVG()会自动忽略NULL值,所以CASE没匹配的行不参与计算。比用WHERE分两次查询再JOIN,性能高3倍以上。
3.4 第四步:验证与发布——让每一份报表都有“出生证明”
最后一步常被跳过,却是信任的基石。我要求所有多维聚合报表必须附带三份“出生证明”:
血缘报告(Data Lineage):用工具(如Atlan、Manta)或手动SQL生成,说明
dashboard.revenue_by_region指标从哪张事实表、经哪些JOIN和计算得出。例如:fact_sales → dim_location (location_id) → region fact_sales → dim_date (date_key) → year, quarter 计算逻辑:SUM(sales_amt) / SUM(cost_amt)样本验证(Sample Validation):随机抽10条原始记录,手工计算其应归属的聚合单元,并与报表结果比对。比如抽到上海2024年4月15日一笔5800元订单,确认它是否计入“华东-Q2”单元,且金额无截断。
边界测试(Edge Case Test):专门测试极端情况:
- 某城市某季度无销售(应显示0还是NULL?业务要求是0)
- 某产品类目成本为0(毛利率计算会除零,需加
NULLIF(cost_amt,0)防护) - 维度表有新成员(如新开苏州仓),事实表尚未有数据(维度表应保留,报表显示0)
我在某车企项目坚持这三步,上线半年内报表争议率从35%降至2%。因为当业务方质疑“为什么北京Q2数据比我们系统少200万”,我能立刻打开血缘报告,指出是ERP接口漏传了37笔订单,并给出修复时间——而不是陷入“你们数据不准”的扯皮。
4. 高频问题排查手册:那些让你凌晨三点还在改SQL的坑
4.1 问题1:聚合结果与Excel透视表不一致,但SQL看起来完全正确
典型现象:BI报表显示华东Q2销售额1.2亿,而业务用Excel拖拽同一张明细表得出1.23亿,差300万。
排查路径:
- 确认数据源一致性:BI用的是否是最新分区?Excel是否连了测试库?用
SELECT MAX(date_key) FROM fact_sales对比; - 检查NULL值处理:Excel的SUM()自动忽略NULL,而SQL的
SUM()也是,但COUNT(*)和COUNT(column)不同。用SELECT COUNT(*), COUNT(sales_amt) FROM fact_sales WHERE region='华东' AND quarter='Q2'看是否有NULL销售额; - 验证维度映射:Excel里“华东”是否包含所有城市?BI中
dim_location表是否漏了某个城市ID?执行SELECT city FROM dim_location WHERE region='华东'与Excel城市列表比对; - 检查过滤逻辑:Excel是否无意中加了“销售额>0”的筛选?BI的WHERE条件是否遗漏了
sales_amt > 0?
根治方案:在ETL层增加一致性校验脚本,每日比对关键指标与源系统差异,超阈值自动告警。我用Python写的校验器,5分钟内定位90%的差异。
4.2 问题2:下钻时数据“凭空消失”——从区域级到城市级,总和对不上
典型现象:华东区Q2总销售额1.2亿,但下钻到上海、杭州、南京三城,加总只有1.15亿。
根本原因:维度表存在孤儿记录(Orphan Records)——事实表里有location_id=999,但dim_location中没有ID=999的记录。LEFT JOIN后,这些记录变成NULL,被GROUP BY丢弃。
快速诊断SQL:
SELECT f.location_id, COUNT(*) FROM fact_sales f LEFT JOIN dim_location d ON f.location_id = d.location_id WHERE d.location_id IS NULL AND f.quarter = 'Q2' GROUP BY f.location_id;修复动作:
- 立即:在JOIN时改用
RIGHT JOIN dim_location(确保所有维度成员都在),对事实表缺失的用COALESCE填充; - 长期:在ETL中增加
NOT EXISTS检查,对孤儿记录打标并通知数据Owner。
实操心得:所有维度JOIN必须配
ON ... AND d.is_current = 1(SCD Type 2有效标志),否则历史变更会导致重复计数。我见过最惨案例:客户维度表有10个版本,没加is_current,一条订单被算10次。
4.3 问题3:同比环比计算结果为NULL,但数据明明存在
典型现象:2024年Q2销售额有值,但yoy_growth列全为NULL。
常见原因及解法:
| 原因 | 检查SQL | 解决方案 |
|---|---|---|
| 时间维度不完整 | SELECT * FROM dim_date WHERE year IN (2023,2024) AND quarter='Q2' | 补全2023年Q2的维度记录,哪怕只是占位符 |
| JOIN条件不匹配 | ON f.date_key = t.date_key AND t.year = 2023(错误!应关联到2023年同期) | 改用ON f.date_key = t.date_key AND t.year = f.year - 1 AND t.quarter = f.quarter |
| 空值传播 | SELECT (cur.sales - pre.sales) / pre.sales,当pre.sales为0时结果为NULL | 改为NULLIF(pre.sales, 0),再用COALESCE(..., 0)兜底 |
终极防护:在BI工具中,所有同比字段必须设置“空值显示为‘—’”,并在报表底部加注释:“同比基于去年同期可比数据,若无可比数据则不显示”。
4.4 问题4:报表加载极慢,但数据量不大(<1000万行)
性能杀手TOP3:
- 维度表未建索引:
dim_date.date_key、dim_location.location_id必须是主键或唯一索引。用EXPLAIN看执行计划,若出现Seq Scan on dim_date,说明在全表扫描; - 字符串JOIN:事实表用
city_name(VARCHAR)关联维度表,而非location_id(INT)。INT JOIN比VARCHAR快20倍以上; - SELECT * 滥用:BI工具默认查所有字段,但维度表有62个字段,只用到3个。显式写出
SELECT d.city, d.region, t.quarter,减少网络传输。
我的优化清单:
- 维度表全部
CLUSTER到主键上(PostgreSQL); - 事实表
date_key、location_id等外键字段建B-tree索引; - 对高频聚合(如
region+quarter),建物化视图mv_sales_region_qtr,每日凌晨刷新; - 在BI连接池中启用
prepared_statement=true,避免SQL硬解析。
某零售客户按此优化后,响应时间从23秒降至1.8秒,用户抱怨率下降76%。
5. 超越SQL:多维聚合的现代演进与你的能力升级路径
5.1 从SQL到语义层:为什么Looker和Tableau Prep正在取代手写SQL
Part 20讲的“Data Manipulation”,在2024年已不只是写SQL的能力,而是构建可复用、可治理、可协作的语义层(Semantic Layer)。传统SQL的问题在于:同一指标(如“毛利率”)在不同报表里可能有5种写法,导致“数据沼泽”。而语义层强制定义:
- 度量(Measure):
gross_margin = SUM(sales_amt - cost_amt) / SUM(sales_amt) - 维度(Dimension):
region必须来自dim_location.region - 过滤器(Filter):
status = 'completed'是默认过滤,不可取消
我在某SaaS公司落地Looker后,指标定义时间从平均3天/个降至2小时/个,因为所有开发者复用同一套定义,不再自己写公式。更关键的是,当财务部要求“毛利率分母改为净销售额(扣除退款)”,只需改语义层一处,200+报表自动更新——这在过去是不敢想的。
行动建议:如果你还在手写SQL,立即做两件事:
- 把常用指标(GMV、毛利率、复购率)整理成Markdown文档,注明原子粒度、聚合规则、业务定义;
- 用开源工具(如Cube.js或Metabase)搭建最小语义层,把文档变成可执行的YAML定义。
5.2 从聚合到预测:多维特征工程的实战入口
多维聚合的终点,恰是机器学习的起点。你辛苦构建的region_quarter_sales_agg表,就是最优质的特征源。比如预测下季度华东销售额,特征可包括:
- 滞后特征:
LAG(sales_amt, 1) OVER (PARTITION BY region ORDER BY year, quarter)(上季度销售额) - 窗口特征:
AVG(sales_amt) OVER (PARTITION BY region ORDER BY year, quarter ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)(近3季度均值) - 维度交叉特征:
region * is_holiday_season(节假日对各区域影响不同)
我在某外卖平台用此方法,将城市销量预测准确率从MAPE 22%提升至14%。关键不是算法多先进,而是多维聚合提供了稳定、可解释、业务友好的特征基座。
入门步骤:
- 用SQL生成宽表:
SELECT region, quarter, sales_amt, lag_sales, avg_3q, is_holiday FROM ... - 导出为CSV,用Python的
scikit-learn训练XGBoost; - 将预测结果作为新度量,回写到BI报表,形成“预测-实际”双轨对比。
5.3 你的能力坐标:从“SQL民工”到“数据架构师”的跃迁地图
Part 20的价值,不在于教会你某个函数,而在于帮你建立多维数据思维的坐标系。我用一张表总结不同段位的能力差异:
| 能力维度 | 初级(能跑通) | 中级(能优化) | 高级(能设计) |
|---|---|---|---|
| 维度理解 | 知道GROUP BY要写哪些字段 | 能识别维度层级断裂(如省→市→区缺失) | 能设计SCD Type 2处理客户地址变更 |
| 聚合安全 | 会用SUM/AVG | 知道加权平均与行平均区别 | 能设计指标血缘,确保下游可审计 |
| 性能意识 | 知道加索引 | 能用EXPLAIN分析执行计划 | 能设计物化视图+增量刷新策略 |
| 协作能力 | 自己写SQL自己用 | 能写文档说明指标口径 | 能推动业务方共同定义维度词典 |
下一步行动清单:
- 本周:审查你负责的1个核心报表,用本文的“血缘报告”模板画出数据流;
- 本月:选1个高频指标(如GMV),用语义层工具(Metabase免费版)重定义,邀请业务方确认口径;
- 本季:用聚合结果生成1个预测模型,哪怕只是简单线性回归,重点是走通“聚合→特征→预测”闭环。
最后分享个小技巧:每次写完GROUP BY,默念三遍——“这个聚合是否保持了维度完整性?是否可逆?是否可验证?”念到第三遍,90%的坑就避开了。多维聚合不是技术活,是严谨的工程实践,而Part 20,就是你手上那把最趁手的刻刀。