1. 项目概述多维聚合中的数据操作远不止GROUP BY那么简单“Part 20: Data Manipulation in Multi-Dimensional Aggregation”这个标题乍看像教科书某章编号但实际踩中了数据分析和商业智能工程中最常被低估、最易出错、也最具业务价值的一环——当数据不再是一张二维表格而是按时间、地域、产品线、客户分层、渠道来源等多个维度交织展开时我们到底该怎么“动”它不是简单加总不是机械切片而是有策略地重塑、有逻辑地折叠、有边界地填充、有依据地推演。我带过七支不同行业的数据团队从零售的千万级门店日销流水到SaaS企业的百万用户行为埋点再到制造业的设备传感器时序集群所有项目在进入深度分析阶段后无一例外卡在“多维聚合后的再加工”这一步。很多人以为写完GROUP BY region, product_category, month就结束了结果发现同比环比算不准Top N排名跨维度失效空缺维度无法自动补零层级汇总与明细下钻对不上……这些不是SQL语法错误而是对多维数据空间结构理解的断层。本篇不讲基础聚合函数不列枯燥的窗口函数语法表而是还原一个真实场景——某快消品牌要分析Q3华东区新品上市效果原始数据含12个维度省、市、区、渠道类型、门店等级、SKU、包装规格、促销档期、会员等级、新老客标识、下单时段、支付方式需产出5类交叉报表3种动态钻取路径1套异常值标记规则。我会带你从零开始拆解每一步“操作”的底层意图、技术选型依据、参数设计逻辑以及那些只有在凌晨三点调试报表时才会咬牙记下的实操陷阱。2. 多维聚合的本质从表格思维到立方体思维的范式转换2.1 为什么传统SQL思维在这里会失效很多工程师习惯把多维聚合理解为“多字段GROUP BY”这是最危险的认知偏差。举个具体例子你要统计“各城市各品类的月度销售额”直觉写法是SELECT city, category, month, SUM(sales) FROM sales_fact GROUP BY city, category, month;表面看没问题但一旦业务方提出“请补全所有城市×品类×月份的组合即使某组合没有销售记录也要显示0”问题就来了。GROUP BY天然只返回有数据的组合而“补全”本质是构建一个笛卡尔积基底空间再将事实数据映射上去。这不是聚合操作而是空间定义 数据投射。我在某电商项目中就因此返工三次第一次用LEFT JOIN生成全量组合但城市列表来自维表品类列表来自另一张维表JOIN逻辑写错导致组合爆炸第二次改用GENERATE_SERIES配合CROSS JOIN但PostgreSQL版本不支持高维生成第三次才意识到该用OLAP引擎内置的FULL OUTER JOIN语义或预计算的维度骨架表。关键点在于多维聚合的第一步不是写SELECT而是明确定义维度域Dimension Domain——每个维度有哪些合法取值、取值间是否存在层级关系如省→市→区、是否允许空值、是否需要强制补全。这直接决定后续所有操作的可行性。2.2 多维数据空间的三个核心结构特征真正理解多维操作必须掌握以下三个结构性特征它们决定了你选择什么工具、怎么设计模型、甚至如何向业务解释结果稀疏性Sparsity现实世界中绝大多数维度组合是空的。10个维度每个维度平均100个取值理论组合数是10^20而实际有数据的可能不到百万分之一。处理稀疏性不是靠暴力填充而是通过稀疏矩阵存储如Apache Kylin的Cube Segment或延迟物化如Doris的Rollup Table来规避无效计算。层级性Hierarchy维度不是扁平列表而是树状结构。例如“时间”维度包含年→季度→月→周→日“地理”维度包含国家→省→市→区→门店。多维操作的核心能力之一是上卷Roll-up与下钻Drill-down但实现方式差异巨大在星型模型中靠JOIN维表实现在雪花模型中需多层JOIN在OLAP Cube中则由元数据定义层级关系自动处理。我在做某银行风控报表时因未在维度表中明确定义“客户等级”的层级VIP→金卡→普卡→潜在客户导致“按等级汇总逾期率”时系统把“潜在客户”错误归入“普卡”分支偏差达37%。正交性Orthogonality理想情况下各维度相互独立组合无业务约束。但现实中存在强耦合例如“促销档期”只适用于“KA渠道”“会员等级”只对“已注册用户”有效。忽略正交性会导致非法组合污染结果。解决方案不是硬编码过滤而是建立维度约束规则引擎——在ETL阶段标记合法组合或在查询层用CASE WHEN动态屏蔽。某母婴品牌曾因未处理“渠道×促销”耦合将线下门店的满减活动错误计入线上直播GMV引发高层质疑。提示判断一个项目是否真正需要多维聚合能力就看业务需求里是否频繁出现“任意组合筛选”“动态层级切换”“跨维度对比”等表述。如果只是固定几个报表用宽表索引更高效一旦需求变得灵活就必须切换到立方体思维。2.3 工具链选型的底层逻辑不是越新越好而是匹配数据密度与查询模式面对多维聚合工程师常陷入工具崇拜看到别人用Doris就上Doris听说ClickHouse快就堆ClickHouse。但实际选型必须回归两个硬指标数据稀疏度和查询QPS模式。我整理了四类典型场景的决策树场景特征推荐方案关键原因我踩过的坑高稀疏度0.001%填充率 低QPS10次/秒 需复杂下钻Apache Kylin基于预计算Cube对稀疏数据压缩率极高HBase存储成本低初期用Kylin跑实时流因Cube刷新延迟导致T1数据不准后改为离线批处理准实时微批中等稀疏度0.1%~5% 高QPS100次/秒 固定维度组合Doris Rollup智能物化视图自动覆盖高频查询路径MySQL兼容语法降低学习成本未合理设置Rollup粒度导致“省×月”Rollup无法支撑“省×市×月”查询被迫重建低稀疏度10% 超高QPS1000次/秒 简单聚合ClickHouse ReplacingMergeTree列存向量化执行对稠密数据吞吐极强但缺失原生层级支持直接用ReplacingMergeTree建模发现“城市→省份”上卷需手动JOIN维表性能暴跌40%维度动态变化频繁如AB测试新增标签 需快速迭代DuckDB Python内存计算SQLPython生态无缝衔接适合探索性分析但不适合生产服务误将DuckDB用于API后端内存溢出崩溃后改用DuckDB物化结果Redis缓存选型不是技术比武而是成本-效率权衡。某教育SaaS公司曾花三个月迁移至ClickHouse结果发现80%查询集中在“学校×年级×学科”三个固定维度最终用MySQL分区表覆盖索引性能提升2倍运维成本降为零。3. 核心操作详解五类高频多维操作的技术实现与避坑指南3.1 空维度补全Dimensional Scaffolding让“没有数据”也变成有效信息业务最常提的需求“把所有城市都列出来没卖货的显示0”。但“补全”不是目的而是手段——目的是让下游计算如同比、占比有稳定分母。真正的难点在于补全的范围必须可配置、可验证、可追溯。我见过太多团队用硬编码城市列表结果行政区划调整后报表崩盘。实操方案以PostgreSQL为例第一步构建维度骨架表Dimension Scaffold-- 创建城市维度骨架含行政层级与状态 CREATE TABLE dim_city_scaffold AS SELECT city_code, city_name, province_code, province_name, is_active, -- 是否当前有效 valid_from, -- 生效日期 valid_to -- 失效日期 FROM dim_city WHERE is_active true AND valid_to CURRENT_DATE; -- 创建时间维度骨架按业务需求生成 CREATE TABLE dim_month_scaffold AS SELECT TO_CHAR(d, YYYY-MM)::TEXT as month_id, d::DATE as month_start, (d INTERVAL 1 month - INTERVAL 1 day)::DATE as month_end FROM GENERATE_SERIES( 2023-01-01::DATE, 2024-12-01::DATE, 1 month ) AS d;第二步用CROSS JOIN生成全量组合再LEFT JOIN事实表WITH full_combination AS ( SELECT s.city_code, s.city_name, m.month_id, m.month_start FROM dim_city_scaffold s CROSS JOIN dim_month_scaffold m WHERE s.city_code IN (SH, NJ, HZ, SU) -- 限定业务关注城市 ) SELECT fc.city_name, fc.month_id, COALESCE(f.sales_amount, 0) as sales_amount, COALESCE(f.order_count, 0) as order_count FROM full_combination fc LEFT JOIN fact_sales_monthly f ON fc.city_code f.city_code AND fc.month_id f.month_id;关键参数设计逻辑CROSS JOIN范围必须受业务约束如WHERE s.city_code IN (...)否则1000个城市×100个月10万行尚可接受若放开到全国地级市×10年则超亿级必须引入分区裁剪。COALESCE不能简单写COALESCE(f.sales_amount, 0)需结合业务含义退货金额为负应设为GREATEST(COALESCE(f.sales_amount, 0), 0)避免负值干扰。注意补全不是万能药。某物流项目曾要求“补全所有司机×线路×日期”但司机每日跑线路是动态调度的强行补全会产生大量虚假0值误导运力分析。此时应改用“最近7天活跃司机×线路”作为基底更符合业务实际。3.2 动态Top N截断Dynamic Top-N with Ties别让“第10名”成为业务黑洞“查各城市销量Top 10门店”看似简单但LIMIT 10会丢失并列情况。更致命的是业务真正需要的常是“Top 10及并列者”或“Top 10占全市销量80%以上的城市”。这要求操作具备上下文感知能力。实操方案窗口函数进阶用法WITH city_store_rank AS ( SELECT city_code, store_id, sales_amount, -- RANK()处理并列DENSE_RANK()更紧凑ROW_NUMBER()严格唯一 RANK() OVER (PARTITION BY city_code ORDER BY sales_amount DESC) as rank_num, -- 计算累计占比用于动态阈值 SUM(sales_amount) OVER ( PARTITION BY city_code ORDER BY sales_amount DESC ROWS UNBOUNDED PRECEDING ) * 1.0 / SUM(sales_amount) OVER (PARTITION BY city_code) as cum_pct FROM fact_store_daily WHERE dt BETWEEN 2023-09-01 AND 2023-09-30 ), top_n_filter AS ( SELECT city_code, store_id, sales_amount, rank_num, cum_pct, -- 动态阈值取Top 10或累计占比≥80%取更宽松者 CASE WHEN rank_num 10 THEN fixed_top10 WHEN cum_pct 0.8 THEN pct_threshold ELSE excluded END as filter_flag FROM city_store_rank ) SELECT * FROM top_n_filter WHERE filter_flag ! excluded;为什么不用ROW_NUMBER()ROW_NUMBER()给并列值分配不同序号导致“第10名”实际可能是第12家店业务无法接受。RANK()保留并列但需额外处理“第10名有3家并列是否全取”——这就是filter_flag的设计意义用业务规则而非技术规则决策。实操心得在某连锁餐饮项目中我们发现单纯Top 10掩盖了“长尾效应”上海前10店占全市65%但杭州前10仅占32%。于是增加cum_pct字段让业务方自主滑动阈值报表交互性大幅提升。ROWS UNBOUNDED PRECEDING必须显式声明否则某些数据库如旧版MySQL默认RANGE语义对相同销售额会错误聚合。3.3 跨维度比率计算Cross-Dimensional Ratio分母陷阱比想象中更深“华东区新品A的市占率”——这句话隐含三重维度地理华东、时间Q3、产品新品A。但市占率新品A销量 / 华东区所有新品销量。问题来了分母的“所有新品”是否包含同属华东但未上市新品是否包含已退市新品比率计算的本质是定义分母的维度上下文。实操方案用CTE明确分母范围WITH numerator AS ( -- 分子华东区Q3上市的新品A销量 SELECT SUM(sales_amount) as num_value FROM fact_sales f JOIN dim_product p ON f.product_id p.product_id JOIN dim_region r ON f.region_id r.region_id WHERE r.region_name 华东 AND p.is_new_launch true AND p.product_name 新品A AND f.month_id BETWEEN 2023-07 AND 2023-09 ), denominator AS ( -- 分母华东区Q3所有在售新品销量排除退市、未上市 SELECT SUM(sales_amount) as den_value FROM fact_sales f JOIN dim_product p ON f.product_id p.product_id JOIN dim_region r ON f.region_id r.region_id WHERE r.region_name 华东 AND p.is_new_launch true AND p.status on_sale -- 关键限定“在售”状态 AND f.month_id BETWEEN 2023-07 AND 2023-09 ) SELECT n.num_value * 1.0 / NULLIF(d.den_value, 0) as market_share_pct FROM numerator n, denominator d;关键设计点解析NULLIF(d.den_value, 0)防止除零错误但更重要的是分母为0时业务需知道是“无数据”还是“计算逻辑错误”。我们在生产环境增加告警当den_value 0且num_value 0时触发说明分子分母定义冲突。p.status on_sale是业务规则不是技术假设。某次上线前产品团队未同步“新品B因质检暂停销售”导致分母错误包含B市占率虚高22%。提示所有比率类指标必须配套“分母构成明细”报表。我在某车企项目中坚持为每个KPI提供下钻页点击市占率数字即可查看“分母包含哪些车型、哪些区域、哪些时间”业务方信任度显著提升。3.4 层级上卷与下钻Hierarchical Roll-up/Drill-down让“省→市”不只是JOIN多维分析的灵魂在于自由切换粒度。但GROUP BY province和GROUP BY province, city之间不是简单增减字段而是维度层级的导航。问题在于不同数据库对层级的支持差异极大。实操方案以Doris为例展示原生层级能力首先在建模阶段定义维度层级-- 创建物化视图定义地理层级 CREATE MATERIALIZED VIEW mv_sales_geo_hierarchy AS SELECT province, city, district, SUM(sales_amount) as total_sales, COUNT(DISTINCT order_id) as order_cnt FROM fact_sales GROUP BY province, city, district PROPERTIES( force_external_table_query_rewrite true );查询时利用Doris的智能路由-- 自动匹配mv_sales_geo_hierarchy无需指定GROUP BY SELECT province, SUM(total_sales) FROM mv_sales_geo_hierarchy GROUP BY province; -- 下钻到城市级同样走同一MV SELECT province, city, SUM(total_sales) FROM mv_sales_geo_hierarchy GROUP BY province, city;对比传统方案MySQL的缺陷需为每个粒度建单独汇总表agg_province,agg_city,agg_districtETL维护成本翻倍。下钻时需JOIN维表获取上级信息如查“上海销量”需JOIN dim_city ON c.city_id d.city_id而Doris直接在MV中存储province字段减少JOIN。实操心得层级定义必须与业务术语一致。某零售项目将“商圈”定义为district但业务方口头说“片区”导致报表命名混乱。我们强制要求所有维度字段名业务词典词条名。上卷时警惕“重复计算”。某次将“门店级”数据上卷到“城市级”因部分门店归属多个商圈未去重导致城市销量虚高。解决方案在事实表中增加city_id冗余字段确保上卷路径唯一。3.5 异常值标记与归因Anomaly Flagging Attribution从“发现异常”到“定位根因”多维聚合的终极价值不是呈现数据而是驱动决策。“华东区Q3销量下降15%”是结果业务需要的是“为什么下降是上海跌了还是南京跌了是高端线跌了还是入门款跌了是新客少了还是复购低了”。这要求操作具备多维归因能力。实操方案Shapley值简化版适配SQLWITH base_metrics AS ( SELECT 2023-Q3 as period, SUM(sales_amount) as sales_total, COUNT(DISTINCT user_id) as user_cnt, AVG(order_amount) as avg_order FROM fact_sales WHERE month_id BETWEEN 2023-07 AND 2023-09 ), comp_metrics AS ( SELECT 2022-Q3 as period, SUM(sales_amount) as sales_total, COUNT(DISTINCT user_id) as user_cnt, AVG(order_amount) as avg_order FROM fact_sales WHERE month_id BETWEEN 2022-07 AND 2022-09 ), delta_calc AS ( SELECT b.period as curr_period, c.period as prev_period, b.sales_total - c.sales_total as sales_delta, b.user_cnt - c.user_cnt as user_delta, b.avg_order - c.avg_order as avg_order_delta FROM base_metrics b, comp_metrics c ), -- 简化归因按各维度贡献比例分配总变化 attribution AS ( SELECT user_cnt as dimension, (user_delta * avg_order) / NULLIF(sales_delta, 0) as contribution_pct FROM delta_calc UNION ALL SELECT avg_order as dimension, (avg_order_delta * user_cnt) / NULLIF(sales_delta, 0) as contribution_pct FROM delta_calc, base_metrics ) SELECT * FROM attribution;为什么不用机器学习归因Shapley值虽精准但需完整特征矩阵和训练周期。在BI场景中业务可解释性优先于数学严谨性。上述简化公式ΔSales ≈ ΔUsers × AvgOrder Users × ΔAvgOrder业务方一眼看懂销量变化新客变化带来的影响 客单价变化带来的影响。实操心得归因必须绑定维度。某次分析发现“新客减少”是主因但未下钻到“新客减少发生在哪些城市”导致市场部无法精准投放。我们在归因结果后强制追加TOP 3 city by user_delta子查询。设置归因阈值。当某维度贡献5%标记为“次要因素”避免业务方过度解读噪音。4. 实操全流程从原始日志到多维分析报表的7步落地4.1 步骤1维度建模审计Dimensional Modeling Audit在写任何聚合SQL前必须完成维度建模审计。这不是文档工作而是技术动作。我用一张检查表驱动检查项检查方法合格标准不合格案例维度完整性SELECT COUNT(*) FROM dim_product WHERE product_id IS NULL0某批次ETL漏传product_id导致事实表JOIN后整行丢失维度时效性SELECT MAX(valid_to) FROM dim_product≥当前日期产品表valid_to最大值为2022-12-31新上市产品无记录维度层级一致性SELECT province, COUNT(DISTINCT city) FROM dim_city GROUP BY province HAVING COUNT(DISTINCT city) 0无结果某省下无城市记录上卷时报错事实表外键有效性SELECT COUNT(*) FROM fact_sales f LEFT JOIN dim_product p ON f.product_id p.product_id WHERE p.product_id IS NULL0事实表存在脏数据product_idUNKNOWN未在维表中定义关键动作所有检查必须自动化为ETL前置校验脚本失败则阻断任务。我在某金融项目中将此检查嵌入Airflow DAG每次调度前运行拦截了87%的数据质量问题。4.2 步骤2稀疏度探查与基底设计Sparsity Profiling运行以下探查SQL决定补全策略-- 计算各维度组合的实际填充率 WITH combo_stats AS ( SELECT COUNT(*) as total_combos, COUNT(DISTINCT CONCAT(city_code, |, category_id, |, month_id)) as filled_combos FROM fact_sales ) SELECT filled_combos * 1.0 / total_combos as sparsity_ratio, CASE WHEN filled_combos * 1.0 / total_combos 0.001 THEN Highly Sparse: Use OLAP Cube WHEN filled_combos * 1.0 / total_combos 0.01 THEN Moderate: Use Materialized View ELSE Dense: Use Indexed Fact Table END as recommendation FROM combo_stats;经验技巧对高稀疏数据绝不尝试CROSS JOIN全量补全。某次处理10维数据理论组合10^15我们改用“采样插值”先对高频组合占80%流量全量补全低频组合用最近邻插值估算。稀疏度会随时间变化。每月运行一次探查动态调整策略。4.3 步骤3维度骨架生成Scaffold Generation基于审计结果生成维度骨架# 使用Python脚本动态生成非SQL硬编码 # 输入维度配置文件 dimensions.yaml # 输出dim_city_scaffold.sql, dim_month_scaffold.sql 等 python generate_scaffold.py --config dimensions.yaml --output sql/dimensions.yaml示例city: source_table: dim_city filters: [is_active true, valid_to current_date] fields: [city_code, city_name, province_code] month: type: date_series start: 2023-01-01 end: 2025-12-01 interval: 1 month fields: [month_id, month_start, month_end]为什么不用纯SQL生成因为业务规则会变某次行政区划调整需新增“直辖市下属区”层级纯SQL需人工修改所有CROSS JOIN而YAML配置只需更新fields脚本自动生成。4.4 步骤4多维聚合SQL编写Multi-Dimensional Aggregation SQL遵循“原子化可组合”原则-- 原子化每个CTE只做一件事 WITH sales_base AS ( -- 基础过滤与清洗 SELECT city_code, category_id, month_id, GREATEST(sales_amount, 0) as sales_amount -- 清洗负值 FROM fact_sales WHERE sales_amount IS NOT NULL ), sales_scaffolded AS ( -- 补全 SELECT s.city_code, s.category_id, s.month_id, COALESCE(b.sales_amount, 0) as sales_amount FROM dim_city_scaffold s CROSS JOIN dim_category_scaffold c CROSS JOIN dim_month_scaffold m LEFT JOIN sales_base b ON s.city_code b.city_code AND c.category_id b.category_id AND m.month_id b.month_id ), sales_analyzed AS ( -- 分析计算 SELECT city_code, category_id, month_id, sales_amount, LAG(sales_amount, 1) OVER ( PARTITION BY city_code, category_id ORDER BY month_id ) as last_month_sales FROM sales_scaffolded ) -- 最终输出只在此处定义业务逻辑 SELECT city_code, category_id, month_id, sales_amount, (sales_amount - last_month_sales) * 1.0 / NULLIF(last_month_sales, 0) as mom_growth_rate FROM sales_analyzed;关键纪律CTE命名体现意图sales_base,sales_scaffolded而非技术动作t1,t2。所有计算放在最后CTE前面只做数据准备。4.5 步骤5性能压测与索引优化Performance Benchmarking对核心查询进行三轮压测冷启动压测清空缓存后首次执行观察是否OOM或超时。热缓存压测重复执行10次取P95耗时确认稳定性。并发压测模拟50并发观察CPU/IO瓶颈。索引优化口诀WHERE字段必建索引如month_id,city_codeGROUP BY字段按顺序建联合索引INDEX idx_geo_time (city_code, category_id, month_id)高基数维度如user_id不单独建索引改用分区某次优化中为fact_sales表增加(city_code, month_id)联合索引Q3查询从23s降至0.8s。4.6 步骤6结果验证与业务对齐Result Validation验证不是比数字而是比逻辑验证维度方法示例总量一致性对比源表SUM与聚合表SUMSELECT SUM(sales_amount) FROM fact_salesvsSELECT SUM(sales_amount) FROM agg_city_month维度完整性检查补全后城市数是否等于业务清单SELECT COUNT(DISTINCT city_code) FROM agg_city_month 12业务确认华东12城业务逻辑正确性抽样人工核验随机选3个城市×3个月手工计算验证同比公式独家技巧在报表前端增加“验证开关”点击即显示该单元格的底层SQL和原始数据行业务方自查时信任感倍增。4.7 步骤7监控告警与迭代闭环Monitoring Iteration上线后必须监控数据新鲜度SELECT MAX(month_id) FROM agg_city_month若超过24小时未更新则告警空值率突增SELECT COUNT(*) FILTER (WHERE sales_amount 0) * 1.0 / COUNT(*) FROM agg_city_month若30%触发调查查询失败率Prometheus采集Presto/Doris查询失败日志5%告警迭代闭环机制每周收集团队反馈分类为Bug类如计算错误24小时内修复体验类如加载慢放入下月优化排期需求类如新增维度评估后纳入季度规划某电商项目通过此机制6个月内将多维报表平均响应时间从8.2s降至1.4s业务方使用率提升300%。5. 常见问题与排查技巧实录那些凌晨三点教会我的事5.1 问题1补全后数据量爆炸查询直接超时现象补全脚本运行后full_combination表达千万级LEFT JOIN事实表后查询超时。排查思路先查EXPLAIN ANALYZE确认是否走索引。若显示Seq Scan on fact_sales说明缺少city_code, month_id联合索引。检查CROSS JOIN范围。用SELECT COUNT(*) FROM dim_city_scaffold确认城市数若超500需业务确认是否真需全部。查事实表数据分布SELECT COUNT(*) FROM fact_sales WHERE city_code IN (SELECT city_code FROM dim_city_scaffold)若为0说明维度表与事实表ID不匹配。根本解决业务侧与PM确认“华东区”是否真指所有地级市还是仅核心12城。技术侧改用IN子查询替代CROSS JOIN让数据库优化器选择Nested LoopSELECT s.city_code, m.month_id, COALESCE(f.sales_amount, 0) FROM dim_city_scaffold s CROSS JOIN dim_month_scaffold m LEFT JOIN fact_sales f ON f.city_code s.city_code AND f.month_id m.month_id AND s.city_code IN (SH,NJ,HZ) -- 限定范围5.2 问题2Top N结果跨维度不一致业务方质疑数据质量现象“各城市Top 10门店”总和为120家但“全国Top 10门店”中只有5家在城市Top 10中出现。原因分析RANK()在不同PARTITION BY下独立排序不存在全局序号。业务混淆了“局部最优”与“全局最优”。解决方案提供双视角报表局部视角各城市内排名用于城市经理考核全局视角全国统一排名用于总部资源调配交叉透视新增“是否进入城市Top 10”布尔列让业务方自主筛选代码实现WITH global_rank AS ( SELECT store_id, RANK() OVER (ORDER BY SUM(sales_amount) DESC) as global_rank_num FROM fact_sales GROUP BY store_id ), city_rank AS ( SELECT city_code, store_id, RANK() OVER (PARTITION BY city_code ORDER BY SUM(sales_amount) DESC) as city_rank_num FROM fact_sales GROUP BY city_code, store_id ) SELECT c.city_code, c.store_id, c.city_rank_num, g.global_rank_num, CASE WHEN g.global_rank_num 10 THEN Yes ELSE No END as in_global_top10 FROM city_rank c LEFT JOIN global_rank g ON c.store_id g.store_id;5.3 问题3同比计算结果为NULL但业务坚称有数据现象LAG(sales_amount, 12) OVER (...)返回NULL但业务说去年同月有销售。排查步骤检查时间维度骨架是否覆盖去年SELECT MIN(month_id), MAX(month_id) FROM dim_month_scaffold检查事实表是否有去年数据SELECT COUNT(*) FROM fact_sales WHERE month_id 2022-09检查PARTITION BY字段是否包含影响排序的维度若PARTITION BY city_code, category_id但去年该城市无此品类则LAG为NULL。终极解决改用FIRST_VALUERANGE窗口强制取时间上最近的有效值FIRST_VALUE(sales_amount) OVER ( PARTITION BY city_code, category_id ORDER BY month_id RANGE BETWEEN INTERVAL 11 months PRECEDING AND INTERVAL 11 months PRECEDING ) as last_year_sales5.4 问题4多维报表加载缓慢前端白屏超10秒现象BI工具
网站建设
高端定制
企业官网