
1. 项目概述当数据聚合从“加总”走向“空间折叠”你有没有遇到过这样的场景销售报表里区域经理要按“省份→城市→门店”三级下钻看毛利财务总监却需要把同一份数据按“产品线→季度→销售渠道”交叉透视算回款率而风控团队又得在“客户等级×逾期天数×放款机构”构成的立方体里揪出异常组合这不是需求混乱而是现代数据分析中一个再真实不过的日常——多维聚合Multi-Dimensional Aggregation早已不是简单的GROUP BY它是一场对数据结构的空间重构。本篇标题中的“Part 20: Data Manipulation in Multi-Dimensional Aggregation”表面看是教程序列的第20节实则踩在了数据工程与分析实践的关键分水岭上从单表统计迈向真正的OLAP式思维。它不教你怎么写SUM()而是告诉你当维度超过两个、指标需要跨层级计算、聚合结果还要被反复切片钻取时“操纵数据”Data Manipulation的本质是设计一种可伸缩、可追溯、可复用的多维数据模型操作范式。我带过的十几个BI落地项目里83%的性能瓶颈和逻辑错误都卡在这一环——不是SQL写不对而是根本没想清楚“维度”和“聚合”之间那层隐含的拓扑关系。这篇文章适合三类人正在啃《深入理解OLAP》的技术新人、天天和Power BI/Tableau搏斗却总被业务方质疑“为什么这个数字和我Excel里不一样”的分析师、以及需要给下游提供稳定宽表服务的数据平台工程师。你不需要会写MDX但必须理解维度不是标签是坐标轴聚合不是运算是空间投影而Manipulation就是在这套坐标系里做平移、旋转、缩放和剖切的全套动作。2. 多维聚合的核心设计逻辑为什么不能只靠SQL GROUP BY2.1 传统GROUP BY的三大结构性失能很多人以为“多维聚合多个字段一起GROUP BY”比如SELECT province, city, product_type, SUM(sales) FROM sales GROUP BY province, city, product_type。这在二维如省产品时看似可行但一旦维度增加到三维及以上问题立刻暴露。我曾接手一个电商数据平台原始聚合语句是GROUP BY category, subcategory, brand, sku, week_start_date, channel——整整6个维度字段。表面看逻辑清晰实际运行后发现三个致命缺陷第一结果集爆炸性膨胀。SKU有50万品牌2000个周粒度52周仅这三项组合就可能产生50万×2000×52≈520亿行潜在组合。即使实际销售记录只有2000万行数据库仍需扫描全表生成中间笛卡尔积内存溢出成常态。PostgreSQL的work_mem调到8GB也扛不住最后被迫加LIMIT结果业务方说“缺了上周华东区某小众品牌的销量报表不准”。第二层级关系完全丢失。province和city天然存在“省包含市”的上下级关系但SQL的GROUP BY把它们扁平化为并列字段。当业务要“查看广东省所有城市销量总和”时你得额外写WHERE province广东再SUM无法像OLAP那样直接“上卷”Roll-up。更麻烦的是如果某城市数据缺失如新设城市未录入GROUP BY结果里压根不会出现该城市导致“广东总销量”自动变小——这不是计算错误是维度完整性被SQL语法隐式破坏。第三指标计算失去上下文感知。假设要计算“各城市的毛利率”公式是(SUM(revenue) - SUM(cost)) / SUM(revenue)。但如果某个城市某周SUM(revenue)0分母为零整个聚合结果就变成NULL。传统方案要么加CASE WHEN兜底要么在应用层处理但问题在于这个“0收入”是真实业务现象如城市刚开业还是数据采集断点GROUP BY无法标记这种语义差异导致后续所有分析都建立在不可信的NULL基础上。提示GROUP BY本质是“无状态哈希分组”它不维护维度间的层次、不校验成员完整性、不区分空值语义。把它当多维聚合工具就像用螺丝刀当电钻——能转但效率低、易打滑、还伤材料。2.2 多维聚合的正确打开方式立方体Cube思维真正解决上述问题的是构建多维数据立方体OLAP Cube。这不是指某个具体工具而是一种建模思想把数据想象成一个N维空间每个维度Dimension是一条坐标轴每个指标Measure是在该空间中的一个数值场。例如销售数据立方体可定义为维度轴地理省→市→区、时间年→季→月→周→日、产品大类→子类→品牌→SKU、渠道线上APP→线下门店→分销商指标场销售额、订单量、退货率、平均客单价空间点每一个具体的广东, 深圳, 南山区, 2024-Q2, 手机, 苹果, 线上APP组合对应一个销售额数值关键突破在于立方体预计算Pre-aggregation与按需计算On-demand Calculation的混合策略。我们不会真的存储所有520亿个点而是按业务高频路径预先计算关键聚合层预计算层级维度组合存储行数估算典型查询场景基础明细层SKU日渠道2000万行查某SKU昨日销量中间聚合层品牌周大类2000×52×50≈520万行品牌周环比分析顶层汇总层省季渠道34×4×3≈400行高管季报PPT这个分层不是随意定的。我用过一套经验法则“3-5-7”黄金比例——任意一个维度其最细粒度成员数如全国34个省级行政区乘以业务最常查询的时间跨度如最近5个季度乘以核心指标数如7个关键KPI结果若小于1000万就值得预计算否则采用实时计算缓存。这套法则在我们给某连锁药店做的项目中把报表平均响应时间从12秒压到1.8秒且99%的查询命中预计算层。2.3 维度建模的底层契约星型模型与雪花模型的选择哲学立方体要落地必须依赖规范的维度建模。主流有两种模式星型Star Schema和雪花型Snowflake Schema。很多教程说“星型简单雪花型节省空间”但这只是表象。真实决策依据是查询模式与变更频率的博弈。星型模型把所有维度表如dim_time,dim_product直接关联到事实表fact_sales维度表是冗余的宽表。例如dim_product包含product_id,brand,category,subcategory所有字段。优势是JOIN少查询快劣势是当品牌信息变更如苹果公司收购某耳机品牌所有历史记录的brand字段都要更新——这是灾难性的。雪花型模型则把维度进一步规范化比如dim_product只存product_id和subcategory_id再通过dim_subcategory表关联到category。这样品牌变更只需改dim_brand表一行历史事实不受影响。但每次查询要多JOIN 2-3张表性能下降30%-50%。我的实战选择标准很直白看维度属性的“静态性”。地理维度省/市代码和时间维度年月日几乎永不变更必须用星型——因为查询频次极高且变更成本为零。而产品维度中品牌、供应商这类属性变更频繁就该用雪花型把dim_brand单独拆出。我们在某母婴电商项目中把产品维度拆成dim_product固定属性SKU、规格、上架日期dim_brand动态属性品牌名、所属集团、认证状态既保证了历史数据一致性又让运营人员能随时更新品牌信息上线后客服投诉“数据和ERP不一致”的工单下降了76%。3. 核心操作解析多维聚合中的四大关键Manipulation动作3.1 上卷Roll-up从细节到概览的智能压缩上卷不是简单求和而是在维度层级树上向上移动并保持指标语义正确。例如从“深圳南山区”上卷到“深圳市”销售额直接相加没问题但“平均客单价”就不能直接平均而要用SUM(销售额)/SUM(订单量)重新计算——这是加权平均不是算术平均。实操中最大的坑是忽略层级完整性检查。比如某省只有A、B两市有数据C市因系统故障缺失。上卷到省时如果直接SUM两市数据结果会比真实值小。正确做法是在构建维度表时强制补全所有合法层级组合。我们用Python脚本自动生成地理维度全集# 生成中国所有省-市组合含空数据占位符 import pandas as pd from itertools import product provinces [广东, 江苏, 浙江, ...] # 全国34省 cities_by_province { 广东: [广州, 深圳, 珠海, 汕头], 江苏: [南京, 苏州, 无锡, 常州], # ... 其他省 } # 生成全量组合每行代表一个合法的省,市坐标 all_combinations [] for p in provinces: cities cities_by_province.get(p, []) if not cities: # 若某省无市数据补一个未知 cities [未知] for c in cities: all_combinations.append({province: p, city: c}) dim_geo pd.DataFrame(all_combinations) dim_geo.to_parquet(dim_geo_full.parquet, indexFalse)这样无论事实表是否包含某市数据维度表都保证该坐标存在。上卷时缺失市的数据自动为0省总和才真实反映“应有规模”。这个脚本我们已封装成Airflow任务每天凌晨自动跑一次确保维度主数据永远完整。3.2 下钻Drill-down从汇总到细节的精准穿透下钻的挑战不在技术而在权限与性能的平衡。业务方常要求“点击省销量下钻看到所有城市”但如果某省有200个城市一次查200行没问题但若用户误点“全国”就要查34×200≈6800行前端渲染卡顿。我们的解法是动态限制下钻深度并用异步加载兜底。在BI工具如Superset中配置下钻动作时不直接关联明细表而是指向一个预计算的“下钻视图”-- 创建下钻专用视图强制限制返回行数 CREATE VIEW sales_city_drilldown AS SELECT province, city, SUM(sales_amt) as sales_amt, COUNT(*) as order_cnt FROM fact_sales_daily f JOIN dim_geo g ON f.geo_id g.geo_id GROUP BY province, city HAVING COUNT(*) 10 -- 过滤掉数据极少的城市防噪声 ORDER BY sales_amt DESC LIMIT 200; -- 强制最多200行避免OOM同时在前端加一层“懒加载”首次点击只显示TOP 20城市底部放“加载更多”按钮点击后发新请求参数带offset20limit20。这样既满足快速浏览又防止单次查询拖垮数据库。这个设计在某银行项目中让客户经理下钻查看网点业绩的平均耗时从8.2秒降到1.4秒。3.3 切片Slicing与切块Dicing多维空间的手术刀切片Slicing是固定一个维度值观察其他维度变化如“只看2024年Q2的数据”。切块Dicing则是同时固定多个维度值如“只看2024年Q2、广东省、手机品类的数据”。很多人混淆二者其实关键区别在于切片产生子立方体Sub-cube切块产生超立方体切片Hyper-slice。技术实现上切片用WHERE过滤即可但切块必须用IN或JOIN确保维度值组合存在。例如要查“苹果、华为、小米”三个品牌在“北京、上海、深圳”的销量不能写-- 错误会产生笛卡尔积查出3×39个组合但其中苹果,北京可能无数据 WHERE brand IN (苹果,华为,小米) AND city IN (北京,上海,深圳)正确做法是先构造合法组合再JOIN事实表-- 正确显式定义要查的品牌,城市组合 WITH target_combos AS ( SELECT 苹果 as brand, 北京 as city UNION ALL SELECT 苹果, 上海 UNION ALL SELECT 华为, 北京 -- ... 显式列出所有需要的9个组合而非3×3 ) SELECT tc.brand, tc.city, COALESCE(SUM(f.sales_amt), 0) as sales_amt FROM target_combos tc LEFT JOIN fact_sales_daily f ON tc.brand f.brand AND tc.city f.city AND f.week_year 2024 AND f.week_qtr Q2 GROUP BY tc.brand, tc.city;这个写法确保只查9个明确组合哪怕其中7个无数据结果也是9行2行有值7行sales_amt0完全可控。我们在某快消品公司的促销分析中用此法将“指定N个SKU在M个门店的活动效果”查询的准确率从63%提升到100%因为再也不用猜“系统到底返回了哪几个组合”。3.4 旋转Pivoting让维度在行列间自由流动旋转是把某个维度的值“摊开”成列比如把channel渠道从行变成列生成sales_app,sales_store,sales_distributor三列。传统用CASE WHEN硬编码但维度值一变如新增“直播带货”渠道就得改SQL。高阶玩法是动态列生成。我们用Presto的map_agg和transform函数实现-- Presto动态旋转自动适配渠道维度的所有值 SELECT province, city, -- 将channel→sales_amt映射转为MAP再提取指定key transform_values( map_agg(channel, sales_amt), (k, v) - CAST(v AS DECIMAL(18,2)) ) AS channel_sales_map FROM fact_sales_daily WHERE week_year 2024 AND week_qtr Q2 GROUP BY province, city结果中channel_sales_map是一个MAP类型如{APP: 125000.00, STORE: 89000.00, DISTRIBUTOR: 45000.00}。下游应用如Python Pandas可直接pd.json_normalize()展开成宽表。这样渠道维度增减都不用动SQL只改应用层解析逻辑。该方案在某跨境平台上线后市场部新增“TikTok Shop”渠道时BI报表零修改自动生效节省了原本2天的开发排期。4. 实操全流程从原始日志到可交互多维立方体4.1 数据准备清洗与标准化的不可妥协环节多维聚合的成败80%取决于输入数据的质量。我们绝不跳过这一步。以某物流公司的GPS轨迹日志为例原始数据长这样device_idtimestamplatlngspeedstatusDEV-0012024-05-01 08:23:1522.54321114.0987645movingDEV-0012024-05-01 08:23:1822.54325114.0988248moving问题极多时间戳无时区、经纬度精度超5位GPS噪声、speed单位不统一有时km/h有时m/s、status值不规范有moving,idle,stop,parked等6种写法。我们的清洗流水线分四步全部用Spark SQL实现可直接部署到EMR/Databricks第一步时间标准化-- 统一转为UTC再转为业务时区如Asia/Shanghai SELECT device_id, from_utc_timestamp(to_utc_timestamp(timestamp, UTC), Asia/Shanghai) as event_time, ...第二步空间去噪-- 用DBSCAN聚类识别异常点速度突变、位置跳跃 -- 此处用Spark MLlib的VectorAssembler DBSCAN略去代码 -- 结果标记outlier_flag: true/false第三步状态归一化SELECT ..., CASE WHEN status IN (moving,run,driving) THEN MOVING WHEN status IN (idle,stop,parked) THEN IDLE ELSE UNKNOWN END as status_clean第四步维度键生成-- 关联地理围栏表生成geo_id如GEO-001深圳南山科技园 SELECT f.*, COALESCE(g.geo_id, GEO-999) as geo_id -- 无围栏则标为未知区域 FROM cleaned_log f LEFT JOIN dim_geo_fence g ON ST_Contains(g.fence_wkt, ST_Point(f.lng, f.lat))这套清洗逻辑被封装成Delta Lake的bronze→silver流水线每天自动运行。关键经验清洗规则必须版本化管理。我们用Git管理SQL脚本每次变更都打tag如clean-v2.3.1确保任何一份聚合结果都能追溯到精确的清洗版本。某次审计发现Q1数据偏差30分钟内就定位到是v2.2.0版漏处理了一种status值立刻回滚修复。4.2 立方体构建预计算层的分层实现策略清洗后的银表silver table是事实表基础。我们按“3-5-7”法则构建三层预计算L1层原子事实表Atomic Fact表名fact_gps_raw字段device_id,event_time,geo_id,status_clean,speed_kmh,duration_sec特点不做任何聚合1:1对应清洗后日志分区键dt20240501供溯源和调试L2层轻度聚合表Lightly Aggregated表名fact_gps_hourly聚合逻辑按device_idgeo_idhour(event_time)分组计算sum(duration_sec)→ 总停留时长count(*)→ 轨迹点数avg(speed_kmh)→ 平均速度max(case when status_cleanMOVING then 1 else 0 end)→ 是否移动过分区键dt20240501,hr08单日约500万行查询延迟200msL3层业务主题宽表Business Wide Table表名fact_route_summary构建逻辑基于L2表关联设备维度dim_device、地理维度dim_geo、时间维度dim_time生成route_id设备日期唯一标识start_geo_id,end_geo_idtotal_duration_min,total_distance_km需用Haversine公式计算is_full_route起点终点不同且停留30min分区键route_date2024-05-01单日约20万行支撑95%的业务报表所有表均用Delta Lake格式存储启用OPTIMIZE和ZORDER BY (geo_id, route_date)提升查询性能。实测表明对fact_route_summary查“某区域昨日所有车辆路线”响应时间稳定在350ms内比直接查L1表快12倍。4.3 查询服务REST API与BI工具的双通道交付立方体建好必须让业务方用起来。我们提供两种接口API通道给开发者/自动化系统用FastAPI构建端点如GET /api/v1/routes?geo_idGEO-001date_from2024-05-01date_to2024-05-05关键设计参数强校验 查询熔断app.get(/routes) def get_routes( geo_id: str Query(..., regexr^GEO-\d{3}$), # 强制格式校验 date_from: date Query(...), date_to: date Query(...), limit: int Query(100, le1000) # 严格限制最大返回行数 ): # 计算时间跨度超7天则拒绝 if (date_to - date_from).days 7: raise HTTPException(400, Date range cannot exceed 7 days) # 构建SQL走L3表 sql fSELECT * FROM fact_route_summary WHERE geo_id{geo_id} AND route_date BETWEEN {date_from} AND {date_to} LIMIT {limit} return run_query(sql)BI通道给业务分析师在Superset中创建数据集源表指向fact_route_summary预置关键仪表板热力地图用geo_id关联地理编码展示各区域车辆停留时长TOP10时效分析X轴route_dateY轴avg(total_duration_min)按device_type货车/轿车分色异常检测用is_full_route0筛选出“疑似无效路线”自动邮件告警所有仪表板设置cache_timeout3005分钟并开启async_queryTrue避免用户等待。某次大促期间API日均调用量达24万次BI并发用户峰值127人系统零故障。5. 常见问题排查与避坑指南血泪总结的12个实战陷阱5.1 维度值漂移Dimension Drift最隐蔽的杀手现象某日突然发现“广东省销量”比前一天暴涨300%但业务确认无大促。排查发现dim_geo表中“广东省”的province_code从CN-GD被误更新为CN-GUANGDONG导致新入库的事实数据全部关联到新code而旧数据还在老code下查询时WHERE province_code LIKE CN%匹配了两个code重复计算。根因维度表未启用主键约束和变更审计。province_code本应是不可变代理键Surrogate Key却被当成自然键Natural Key直接更新。解决方案维度表必须用surrogate_key如geo_sk BIGINT作为主键province_code仅为属性字段所有ETL任务禁止UPDATE维度表只允许INSERT新记录SET is_currentfalse旧记录用SCD Type 2缓慢变化维管理变更每条记录带valid_from/valid_to时间戳我们在某政务大数据平台强制推行此规范后维度漂移类故障从每月3.2起降至0。5.2 时间维度错位跨时区业务的定时炸弹现象跨国电商的“日销量”报表美国站和中国站数据总对不上。查日志发现美国站数据按America/Los_Angeles时间入库中国站按Asia/Shanghai但聚合时统一用DATE(event_time)导致同一天交易被分到两天。根因未统一业务时间Business Time与处理时间Processing Time。业务上“2024-05-01的销量”应指所有时区在该日00:00-23:59发生的交易而非服务器本地时间。解决方案在事实表中除原始event_time外必加business_date字段由ETL统一转换-- 所有数据无论来源时区都转为UTC再转为业务基准时区如Asia/Shanghai to_date(from_utc_timestamp(to_utc_timestamp(event_time, source_tz), Asia/Shanghai)) as business_date报表查询一律用business_date禁用DATE(event_time)这个改动让某跨境电商的全球日销报表准确率从89%升至100%。5.3 指标语义污染一个NULL引发的雪崩现象风控模型报警“某区域退货率突增至99%”人工核查发现该区域当日只有1笔订单且退货了但分母订单量为1分子退货量为1计算无误。问题在于单笔订单的退货率毫无统计意义却混入了区域级聚合。根因聚合时未设置最小样本量阈值Minimum Sample Size。多维聚合必须定义“有效聚合”的底线。解决方案在L3宽表中为每个指标添加sample_size字段如order_cnt查询时强制过滤WHERE order_cnt 10区域级或WHERE order_cnt 50省级对低于阈值的组合指标值设为NULL并加注释字段metric_statusINSUFFICIENT_SAMPLE我们在某保险公司的理赔分析中实施此规则后管理层决策会议上的“异常数据争议”减少了70%。5.4 预计算失效缓存与事实的撕裂现象某日运营说“昨天的销量数据还没出来”查发现L3表fact_route_summary的route_date2024-05-01分区为空。但L1表里明明有5月1日的数据。根因L2→L3的ETL任务依赖L1表的dt分区但L1表的dt20240501分区因网络问题延迟2小时才就绪而L2任务已在2024-05-01 02:00触发查不到数据导致L3无输出。解决方案所有ETL任务加上游分区就绪检查Partition Readiness Check# Airflow中L2任务前加传感器 wait_for_l1_partition ExternalTaskSensor( task_idwait_for_l1_partition, external_dag_idingest_gps_logs, external_task_idload_to_bronze, check_existenceTrue, poke_interval300, # 每5分钟检查一次 timeout3600, # 最多等1小时 modereschedule # 等待时释放worker资源 )L3任务再加一层数据质量校验Data Quality Check-- 检查L2表中是否有足够数据 SELECT COUNT(*) FROM fact_gps_hourly WHERE dt 20240501 HAVING COUNT(*) 10000若不满足发告警并暂停L3任务。这套机制让数据就绪率从92%提升到99.99%。5.5 权限失控维度泄露的合规风险现象某销售总监能查到竞品公司的门店销量数据。查日志发现其账号关联了role_sales_director该角色在dim_geo表上有SELECT权限而dim_geo中geo_id包含了所有合作方的门店编码。根因维度表权限粒度太粗未按“数据主权”隔离。地理维度不是公共资产而是按客户归属划分。解决方案维度表按数据主权分库dim_geo_customer_a,dim_geo_customer_b用行级安全Row Level Security, RLS控制访问-- PostgreSQL中创建RLS策略 CREATE POLICY geo_rls_policy ON dim_geo_customer_a USING (customer_id current_setting(app.current_customer_id, true)::TEXT);应用连接数据库时SET app.current_customer_id CUSTOMER_A;此方案在某SaaS服务商上线后彻底杜绝了跨客户数据泄露。6. 工具链选型与性能调优不迷信框架只信数据表现6.1 计算引擎Trino vs Spark SQL的临界点抉择很多人纠结“该用Trino还是Spark”。我的答案很务实看你的事实表大小和查询并发量。如果事实表1TB且日均查询1000次用Trino。它内存计算启动快SQL兼容性好运维简单。我们给某中型零售企业搭的Trino集群3台r6.2xlarge查10亿行销售事实表95%查询3秒。如果事实表5TB且需跑复杂ETL如多表JOIN窗口函数UDF用Spark SQL。它磁盘容错强能处理超大Shuffle且与Delta Lake深度集成。某物流平台事实表达12TBSpark作业稳定运行而同样SQL在Trino上OOM三次。关键参数调优经验Trinoquery.max-memory-per-node16GB避免单节点OOMoptimizer.join-reordering-strategyELIMINATE_CROSS_JOINS防笛卡尔积Sparkspark.sql.adaptive.enabledtrue自适应查询优化spark.sql.adaptive.coalescePartitions.enabledtrue自动合并小文件6.2 存储格式Delta Lake为何成为事实表首选Parquet虽快但缺乏ACID事务和时间旅行Time Travel。Delta Lake解决了三大痛点并发写入安全多任务同时写fact_sales_dailyDelta自动处理冲突无需手动锁表数据回滚某次ETL bug导致错误数据写入执行RESTORE TO VERSION AS OF 123秒级恢复变更数据捕获CDC用DESCRIBE HISTORY fact_sales_daily查到每次写入的operationMetrics精准定位慢查询源头我们在某金融项目中Delta Lake让数据管道的MTTR平均修复时间从47分钟降至3分钟。6.3 BI工具Superset的隐藏性能开关Superset默认配置很保守。我们调优后QPS每秒查询数提升3倍关闭无用功能ENABLE_JAVASCRIPT_CONTROLSfalse禁用JS控件减少前端计算启用查询缓存CACHE_CONFIG指向RedisCACHE_DEFAULT_TIMEOUT300优化SQL LabSQLALCHEMY_ENGINE_OPTIONS{pool_pre_ping: true, pool_recycle: 3600}防连接泄漏最重要的是所有仪表板必须开启“异步查询”。Superset 2.0默认关闭手动在superset_config.py中设GLOBAL_ASYNC_QUERIES True。否则10个用户同时刷仪表板后端直接排队阻塞。7. 个人实战体会多维聚合不是技术活是翻译活带完二十多个项目我越来越确信多维聚合最难的不是写SQL而是把业务语言翻译成维度语言。业务方说“看下华东区最近三个月的爆款”这句话里藏着至少五个待澄清点“华东区”是地理概念江浙沪皖还是组织概念华东大区销售团队管辖范围前者用dim_geo后者用dim_org“最近三个月”是自然月4-6月还是滚动月过去90天影响business_date的计算逻辑“爆款”是销量TOP10还是GMV TOP10还是转化率TOP10指标定义必须前置对齐“看下”是要总数还是要分城市、分渠道的明细决定上卷还是下钻这个需求是临时看一眼还是要固化进日报决定走即席查询还是预计算我现在的标准动作是收到需求立刻拉业务方开15分钟“维度对齐会”用白板画出维度层级树当场确认每个词对应的维度表和字段。会后发邮件确认“根据今日对齐‘华东区爆款’定义为dim_geo.region华东 AND dim_time.month_seq (current_month_seq - 2) AND rank() over (order by sum(sales_amt) desc) 10是否正确”——这封邮件就是后续所有技术工作的唯一依据。技术可以迭代但翻译错了