ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

零售集团BI需求分析实战:从指标口径到SQL验收的完整指南

零售集团BI需求分析实战:从指标口径到SQL验收的完整指南 简介面向零售企业信息化团队与BI项目规划者这份需求分析报告系统梳理了某零售集团BI二期建设的目标与范围。内容从BI定义出发围绕日常业务报表、业务探索式分析OLAP、KPI指标分析三大功能模块展开并给出系统总体流程、报表处理流程及数据说明可作为同类企业开展BI选型、需求调研或方案设计的参考模板。资源仅包含1个docx文件压缩包大小547KB为完整需求分析文档便于直接查看、编辑与复用。目前已有88人学习下载适合商业智能分析师、产品经理及企业信息化负责人阅读。报告层级清晰从总述到分项说明再到运行环境能够帮助读者快速掌握零售场景下BI系统的功能架构与数据流转逻辑也可作为撰写自身需求文档的结构参照。1. 零售集团BI需求分析报告写的是什么一份可执行的指标体系一个零售集团的BI项目推进到需求分析阶段最怕收到两类需求一类是“我要一个销售大屏”另一类是“把每天的日报电子化”。前者没法评估工作量后者只会做成报表垃圾桶。一份名为“BI系统需求分析报告”的文档真正要产出的不是几十页Word而是一套能把业务问题翻译成数据方案的东西指标字典、取数逻辑、分析主题、报表原型和验收口径。它同时约束业务和开发让双方在同一个数字面前不再各说各话。这份报告的读者通常是数据分析师、BI工程师、业务负责人和IT项目经理写报告的人至少要能讲清楚“某个指标在某一天、某家店为什么是这个数”。2. 需求盘点零售BI指标体系、分析主题与数据口径2.1 先按分析主题归类不按部门收集报表零售集团的需求访谈结果经常是一张几百行的“报表清单”分别来自总部、区域、门店以及ERP、POS、CRM多个系统。我一般不会把这些报表请求直接交给开发而是先把它们归并成分析主题。这样做的好处是一个分析主题对应一组指标、一个事实表和一套交互方式后面排期和建模都按主题推进而不是按报表逐张推进。| 分析主题 | 主要使用角色 | 典型问题 | 关键指标示例 | | 经营总览 | 集团高管 | 本月整体销售和毛利是否达标 | 销售额、毛利率、同比、环比 | | 门店运营 | 门店经理、运营督导 | 哪几家店客流量下降 | 店效、坪效、客单价、连带率 | | 商品分析 | 采购、商品部 | 哪些SKU滞销或缺货 | 动销率、售罄率、库存周转天数 | | 会员营销 | 市场部、CRM运营 | 复购率为什么连续下降 | 复购率、会员贡献率、RFM分层 | | 财务分析 | 财务部 | 区域利润差异来自哪里 | 毛利额、净利率、费用率 |把需求归到分析主题之后还要回业务部门做一轮确认每个主题下最多保留5个高频指标超出部分放进“自助分析”范围。这个约定很重要否则报告里指标上百个实际被使用的不足两成项目后期会一直陷在指标维护里。主题优先级可以在需求报告中用P0/P1/P2标出P0一个月上线P1一个季度P2做成自助分析模板。2.2 销售额口径为什么总是对不上指标卡片与边界条件零售BI需求分析报告里最容易被业务挑战的是“数不对”。根源往往不是报表写错而是同一指标没有统一的业务口径。以“销售额”为例一家零售集团内部就可能并存三个版本订单原金额、支付成功金额、剔除退款和测试订单后的可结算金额。口径不写清楚后面无论用Power BI还是FineBI做出来都会有人拿Excel来质疑。常见做法是把每个核心指标写成一张指标卡片字段至少包括业务口径、取数表、过滤条件、聚合粒度、统计时间和备注。以“可结算销售额”为例| 指标项 | 取值 | | 指标名称 | 销售额可结算 | | 业务口径 | 支付成功且未退款的商品金额合计含税、含赠品剔除测试订单 | | 取数表 | dw_sales_order、dw_sales_order_item | | 过滤条件 | order_statuspaid AND refund_status0 AND is_test_order0 | | 聚合粒度 | 订单行 商品 门店 日 | | 统计时间 | pay_time支付时间 |指标卡片不需要覆盖所有极端场景能把核心指标的80%理清即可剩下的边界情况可以在指标字典附录列“暂时不处理”及原因。很多BI项目后期扯皮都是因为报告里少写了“剔除测试订单”这一行字。2.3 Python 3.11.9 SQL 探查字段把口径变成可执行规则指标卡片的过滤条件写完后要去数据仓库确认字段是否真实存在、枚举值是否符合预期。需求分析阶段就可以写一个字段探查脚本下面是一个可参考的探针# data_probe.py # Python 3.11.9 pymysql pandas只读查询不更新任何业务表 import pymysql import pandas as pd conn pymysql.connect( host10.20.1.8, # 数仓地址不要直连业务库 userbi_read, # 只读账号 passwordopen(/etc/bi_secret).read().strip(), databaseretail_dw, charsetutf8mb4, read_timeout120 # 慢查询120秒断开避免挂死 ) sql SELECT o.channel, o.order_status, o.is_test_order, o.refund_status, COUNT(*) AS order_cnt, SUM(oi.amount) AS amount_sum FROM dw_sales_order o LEFT JOIN dw_sales_order_item oi ON o.order_id oi.order_id WHERE o.pay_time 2024-01-01 GROUP BY o.channel, o.order_status, o.is_test_order, o.refund_status ORDER BY o.channel, o.order_status df pd.read_sql(sql, conn) print(df.head(30))这段脚本的逻辑是按渠道、订单状态、测试标记、退款状态分组汇总订单量和金额快速判断order_status有多少种取值、refund_status是否只有0和1、is_test_order是否真的被写入。read_timeout设为120秒防止上线前探查SQL写成全表扫描拖垮数仓密码不要写死在脚本里放到只有部署账号能读的文件或从环境变量读取。探查结果要和指标卡片逐条对照。如果refund_status里出现2代表部分退款就要和业务确认“部分退款算不算款”并在卡片过滤条件里写全。这也说明为什么不能只让业务在Excel里填口径口径必须落到真实字段上才算数。这个阶段多花一天后面报表返工就能少一周。注意探查脚本只允许使用只读账号不要把业务库连接串写进需求报告正文。3. 把BI需求变成数据模型零售数据集市与维度建模3.1 事实表粒度先于指标设计BI需求报告里的每个指标最终都要映射到一张事实表和一个聚合粒度。零售业务最常用的销售订单表有两种粒度订单头粒度是一张订单一行适合算订单数、客单价订单行粒度是一个商品明细一行适合算品类销售额、退款明细。只要需求里出现“按品类看门店销售”订单头粒度就不够用。因此需求分析报告应把每个P0分析主题的数据粒度写清楚。例如“销售分析”采用订单行粒度一行代表一条订单商品明细一张订单有10个SKU就拆成10行。粒度明确后销售额是明细行金额求和连带率是订单数除以购买人数衍生指标都能从同一模型算出来不会出现两个开发人员各建一套表的情况。3.2 维度表决定能怎么切门店、商品、会员、日期事实表回答“发生了什么”维度表回答“从哪些角度切”。零售集团BI报告里高频出现的维度是日期、门店、商品、会员每个维度需要的属性不完全一样。以下是一份常见的维表规划| 维度 | 常用属性 | 决定的分析能力 | 变化处理 | | 日期 | 年、月、周、日、节假日、大促标记 | 同比、环比、大促对比 | 静态维表一次性生成 | | 门店 | 区域、城市、业态、开业日期 | 区域排名、店效对比 | 缓慢变化维Type 2 | | 商品 | 品类、品牌、价格带、上市日期 | 品类分析、价格带分析 | 缓慢变化维Type 2 | | 会员 | 会员等级、注册渠道、年龄段 | 会员贡献率、RFM分层 | 每日快照 |最容易踩的坑是门店和商品维度。门店会换区域归属商品会调整品类直接在主键上更新会丢失历史分析口径。常见做法是给维度表加上生效起始日、失效结束日查询时按发生日期join维度行。需求报告里要写清“按当前归属统计”还是“按发生日归属统计”否则报表里门店区域销售会忽大忽小。3.3 用SQL视图固化可结算销售额避免每张报表各算一遍BI需求落地时最常见的模型问题是同一口径在报表层复制多份Power BI里写一遍度量值FineBI里再写一遍两边查询条件稍微不一致数字就会打架。我一般会把指标卡片里高复用口径下沉到数据仓库层做成视图或实体汇总表报表工具只引用这一层。-- dm_sales_daily.sql -- 建立日级可结算销售额视图供所有报表复用 CREATE VIEW dm_sales_daily AS SELECT DATE(o.pay_time) AS stat_date, o.channel, o.store_id, oi.product_id, SUM(oi.qty) AS sale_qty, SUM(oi.amount) AS sale_amount, SUM(oi.cost_amount) AS cost_amount, SUM(oi.amount - oi.cost_amount) AS gross_profit FROM dw_sales_order_item oi JOIN dw_sales_order o ON oi.order_id o.order_id WHERE o.order_status paid AND o.refund_status 0 AND o.is_test_order 0 GROUP BY DATE(o.pay_time), o.channel, o.store_id, oi.product_id;这段SQL的逻辑是把支付成功、未退款、非测试的订单明细按日期、渠道、门店、商品四个键汇总同时算出成本金额和毛利额。报表端只需要对视图做SUM不需要重复写过滤条件。参数上order_status和refund_status的取值必须与2.3节探查结果核对如果状态枚举不是预期视图出来的数会整体偏小或偏大。视图只适合数据量可控的起步阶段。日明细几十万行时视图性能尚可超过百万行建议改成按天分区的实体汇总表每天凌晨用调度任务刷新。实体表的优点是查询快、对Power BI DirectQuery友好代价是刷新链路多一层需求分析报告里要把刷新时间和业务可用时间写清楚避免业务一早看数时数据还没跑完。3.4 同比环比的时间口径日期维表比函数更可靠零售指标里同比、环比实现不复杂麻烦在节假日和大促。直接在SQL里写DATE_SUB(CURDATE(), INTERVAL 1 YEAR)正常月份没问题但春节促销周期每年不一样固定日期减一年得到的基期没有业务意义。常见做法是预先建好日期维表维护农历日期、节假日、大促标记再按这些标记计算可比同比。需求分析报告应单独留一小节说明“同比基准”。例如“2024年春节同比基准是2023年春节不用自然年周数对齐而用春节前N天对齐”。这一句话落到模型上要新增日期维属性落到报表上要修改时间智能计算。在需求阶段把时间口径锁定后面开发就不用反复改DAX或FineBI计算字段。4. 报表需求与Power BI、FineBI原型的选型验证4.1 四类报表场景决定开发量需求分析报告里的报表看起来都是“看数”实现方式差别却很大。我会把所有报表请求拆成四类经营看板、维度下钻、异动预警、自助分析。经营看板是固定布局适合做首页维度下钻要求点击门店或商品后切换聚合粒度异动预警靠条件格式或订阅推送自助分析给业务一个限定模型内的自助拖拽入口。这个分类决定工具选型和实施排期。如果需求里异动预警很多就要额外评估推送渠道如果自助分析是核心诉求建模阶段就要准备清晰易读的语义层。很多BI项目选型时只看可视化效果后期卡在预警推送和行级权限上往往是因为需求报告没有提前区分这四类场景。4.2 零售BI选型不能只看看板效果Power BI与FineBI的差异零售集团做BI选型时最常提到的是Power BI和FineBI两个工具都能做销售看板适用边界不同。以下是我通常给出的选型对比| 对比维度 | Power BI | FineBIFanruan BI | 说明 | | 数据连接 | 支持MySQL等常用Power BI MySQL Connector/Net | 内置较多常用数据库和接口 | 主要看数仓是什么技术栈 | | 建模能力 | 建模灵活DAX表达力强 | 模型较弱数据准备更向导化 | 复杂指标多的集团Power BI更顺 | | 自助分析 | 需要用户理解表关系和DAX | 业务用户更容易上手 | 取决于运营团队实际能力 | | 部署方式 | 云端服务为主本地部署受限 | 支持本地化部署 | 集团数据不出域时有时必须选FineBI |这张表不是证明谁比谁强而是提醒需求报告必须在“技术架构”章节回答三个问题数据能否出域、业务用户由谁使用、复杂指标有多少。三个问题的答案会指向不同工具。如果数据不能出域Power BI的云共享能力用不上如果业务团队没有专职数据分析师FineBI的拖拽式准备数据能降低上手门槛。4.3 用Power BI MySQL Connector/Net连接零售数仓的最小步骤需求分析阶段通常要做原型验证用一周时间把P0报表的关键数字在BI工具里跑通。以Power BI Desktop连接MySQL数仓为例常见方式是通过MySQL Connector/Net驱动连接。最小步骤如下在数仓侧创建只读账号只授权查询dm_sales_daily所在库在开发机安装MySQL Connector/Net注意位数要和Power BI Desktop一致在Power BI Desktop选择“获取数据 - MySQL数据库”输入数仓地址、库名在高级选项填入带过滤条件的SQL避免一次性拉全表加载到模型后检查列类型和行数再进入建模视图建度量值。原型阶段的高级选项里可以填这样的SQL-- 需求原型阶段按需取数只拉最近90天重点门店 SELECT stat_date, channel, store_id, product_id, sale_qty, sale_amount, cost_amount, gross_profit FROM dm_sales_daily WHERE stat_date DATE_SUB(CURDATE(), INTERVAL 90 DAY) AND store_id IN (1, 2, 3, 4);这段SQL的作用是把90天、4家门店的日汇总先读进Power BI模型验证“可结算销售额”和“毛利额”能否正确展示。参数上90天是为了控制本地模型大小store_id列表来自需求报告试点范围真实上线时应去掉这个限制改用增量刷新。MySQL Connector/Net连接串里常见参数有charset、ssl-mode数仓开启SSL时需要在高级连接属性里指定。提示原型阶段先用90天数据验证口径不要第一次连接就把全表数据拉进模型。4.4 DAX度量原型验证毛利口径是否照进了报表报表原型不能只看汇总数字还要能回答“毛利额为什么和财务口径不一样”。使用已固化好的汇总表时毛利额建议写成SUMX形式销售毛利额 SUMX ( dm_sales_daily, dm_sales_daily[sale_amount] - dm_sales_daily[cost_amount] )SUMX会按行先计算销售金额减成本金额再对所有行求和避免先求和再相减带来的精度差异。很多BI学习资料里常见写法是SUM([sale_amount]) - SUM([cost_amount])在汇总表上结果通常一致但一旦做维度筛选SUMX的行上下文更可控。需求报告里如果明确了“毛利额按订单行计算、成本按移动加权平均”度量值就能直接对应上不用原型阶段再猜口径。5. 让BI需求分析报告可评审验收SQL与自查清单需求分析报告如果只有文字描述评审会大概率变成“数字对不对”的吵架会。我会在报告最后附两份材料一份可执行的指标验证SQL一份口径确认签收表前者管数后者管责。5.1 报告骨架与口径签收表报告章节可以按“目标与范围、干系人与分析主题、指标字典、数据模型、报表清单、工具选型、上线计划”排列。其中指标字典和数据模型两章是后续开发依据评审时应逐行确认。建议让业务方在指标卡片上签署确认人、确认日期、口径版本减少上线后“这个口径我没同意过”的争议。5.2 把口径验收做成SQL下面这段SQL是需求报告附带的验证脚本抽取任意时间段、任意门店按口径汇总销售额。报表开发完成后用它对BI报表导出的数字做全量对比差异超过0.5%就要回到明细表排查-- check_sales_metric.sql -- 评审和验收阶段通用按日、门店核对可结算销售额 SELECT DATE(o.pay_time) AS stat_date, o.store_id, ROUND(SUM(oi.amount), 2) AS sales_amount_by_report FROM dw_sales_order_item oi JOIN dw_sales_order o ON oi.order_id o.order_id WHERE o.order_status paid AND o.refund_status 0 AND o.is_test_order 0 AND o.pay_time 2024-01-01 GROUP BY DATE(o.pay_time), o.store_id ORDER BY stat_date, o.store_id;这段SQL按需求报告口径在数仓明细层直接计算不经过任何BI工具所以可以作为口径基准。参数上有两个地方要确认refund_status若存在部分退款要确认需求定义的是“完全未退款”还是“部分退款也算”is_test_order不是所有系统都有需先和主数据团队确认排除方法。把check_sales_metric.sql放进生产脚本目录每次口径变更只改这一处运行结果就是需求和报表之间最直接的比对依据。| 校验点 | 最常见的坑 | 检查方法 | | 退款状态枚举 | 0、1、2含义在各系统可能不同 | 先跑distinct(refund_status) | | 测试订单 | 测试单混入订单表 | 确认是否存在is_test_order标记 | | 统计时区 | 线上渠道按UTC存储时间 | 统一按北京时间统计日切 |评审时拿这份SQL和BI报表导出结果逐行比对对不上的那一行就是需求报告下一轮修订的位置。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进