目录
挑战FineBI极限的Dashboard看板案例分享 Part 1 综述
挑战FineBI极限的Dashboard看板案例分享 Part 2 设计与架构(1)建模
挑战FineBI极限的Dashboard看板案例分享 Part 2 设计与架构(2)看板设计
挑战FineBI极限的Dashboard看板案例分享 Part 3 图表设计
I 报表形式
本项目如按标准,根据目标用户群体的划分,将需求分拆为满足老板看数需求的Dashboard看板和满足业务高管和业务骨干看数需求的分析报表的项目集开发。但在时间和资源有限的情况下,只能大胆引进“旋转”角度的理念,将分析报表结合进了Dashboard看板(如下图)。
II 指标梳理
整个报表如上图所示,包括了17个指标。并根据不同时段、对比方式等,共计衍生出了50+个指标。其中与订单量相关的指标,如客单价、订单数、单均毛利额、单均销售件数等,在全局筛选情况下属于不可累加指标。
III 建模思路
如前文“挑战FineBI极限的Dashboard看板案例分享 Part 1 综述”中所述,为了更有效重复利用现有组件,每个组件都需要接受全局传参。所以每个组件背后的数据集,都需要17个维度的参数。因此,传统FineBI采用抽取模式并不适合于本项目。采用帆软SQL数据集定义函数,根据筛选条件调整SQL的select(维度和指标)以及where(广度和深度,定义口径)更适合本项目。SQL数据集定义函数在FineReport开发中属于常规操作,FineBI虽然也支持这一功能,但从官方文档提供的信息量差异也可以推测,直联数据下的SQL数据集定义函数并不是FineBI用户的常用选择。
根据区域树(省份⇲城市⇲门店)和机构树(运营1组分组⇲运营2级分组⇲门店)的结构可以看出,门店是这2个树状结构维度共同的最小粒度。同理可以从商品树(商品大类⇲商品中类⇲商品小类⇲商品单成份⇲商品厂牌/品牌⇲商品SKU)的结构看出,SKU是商品树状结构最小颗粒度。
订单量指标在门店粒度是可累加,不会因为膨胀的笛卡尔积影响计算精度;而订单量指标在SKU粒度虽是不可累加指标,但件均价、件均毛利额等指标的计算精度不会受整体订单量膨胀产生的笛卡尔积而影响计算精度。
因此以这2个粒度为主,并将报告类型加入行定义完成建模,可以覆盖绝大多数场景。极个别组件,直接使用dws + dim计算。这样即可以充分发挥预计算的算力优势,又可以快速反映
图表使用的SQL数据集如下
SQL数据集参考如下代码
WITH o1 AS (SELECT fd_store_id FROM qyt02.arko_dim_channel
WHERE fd_channel_property15 = '门店' GROUP BY fd_store_id)
,o2 AS (SELECT fd_store_id FROM qyt02.arko_dim_channel
WHERE fd_channel_property2 = '自营' GROUP BY fd_store_id)
,o3 AS (SELECT fd_store_id FROM qyt02.arko_dim_channel
WHERE fd_channel_property2 = '加盟' GROUP BY fd_store_id)
SELECT
cops.pdt_hier_catcode3 pdt_hier_catcode3
,cops.fd_product_prop14 pdt_prop_factory_brand
,cops.fd_product_prop16 fd_product_prop16
,cops.pdt_code pdt_code
,cops.pdt_barcode pdt_barcode
,cops.pdt_name pdt_name
,IF(cops.fd_category_name4 = '新特药', 'DTP', '其它') DTP
,pdt_prop_key PR
,sum(cops.nbsk) idx_pos_cnt_bsk
,sum(cops.fd_final_amount) idx_pos_amt_fnl
,sum(cops.fd_gross_profit) idx_pos_gpft
,sum(cops.fd_quantity) idx_pos_qty
,sum(cops.nbsk_old) idx_pos_cnt_bsk_old
,sum(cops.fd_final_amount_old) idx_pos_amt_fnl_old
,sum(cops.fd_gross_profit_old) idx_pos_gpft_old
,sum(cops.fd_quantity_old) idx_pos_qty_old
,countIf(distinct cops.org_id, cops.fd_final_amount IS NOT NULL) idx_pos_cnt_org
,countIf(distinct cops.org_id
,cops.fd_final_amount_old IS NOT NULL) idx_pos_cnt_org_old
,idx_pos_amt_fnl / idx_pos_qty idx_pos_pce
,idx_pos_amt_fnl / idx_pos_cnt_bsk amt_fnl_per_bsk
,idx_pos_amt_fnl / idx_pos_cnt_org amt_fnl_per_org
,idx_pos_gpft / idx_pos_qty gpft_per_qty
,idx_pos_qty / idx_pos_cnt_bsk qty_per_bsk
,idx_pos_qty / idx_pos_cnt_org qty_per_org
,idx_pos_amt_fnl_old / idx_pos_qty_old idx_pos_pce_old
,idx_pos_amt_fnl_old / idx_pos_cnt_bsk_old amt_fnl_per_bsk_old
,idx_pos_amt_fnl_old / idx_pos_cnt_org_old amt_fnl_per_org_old
,idx_pos_gpft_old / idx_pos_qty_old gpft_per_qty_old
,idx_pos_qty_old / idx_pos_cnt_bsk_old qty_per_bsk_old
,idx_pos_qty_old / idx_pos_cnt_org_old qty_per_org_old
FROM report.app_qyt_sales_summary cops
WHERE 1
AND cops.rpt_period = '${rpt_period}'
${IF(own_fd_media = "全部", ""
,IF(own_fd_media = "新零售" ,"AND plt_name IN ('实体门店' ,'小明科技' ,'京东到家' ,'百度健康' ,'美团外卖' ,'京东药急送' ,'饿了么' ,'天猫同城达' ,'饿了么B2C' ,'小荷')"
,IF(own_fd_media = "线下" ,"AND plt_name IN ('实体门店' ,'小明科技')"
,IF(own_fd_media = "O2O" ,"AND plt_name IN ('京东到家' ,'美团外卖' ,'京东药急送' ,'饿了么' ,'天猫同城达' ,'饿了么B2C' ,'百度健康' ,'小荷' ,'美团中心店')"
,IF(own_fd_media = "B2C" ,"AND plt_name IN ('京东' ,'天猫' ,'唯品会' ,'拼多多' ,'百度B2C' ,'美团B2C' ,'抖音')" ,"")))))}
${IF(own_plt_name = "全部", "" ,"AND cops.plt_name = '"+ own_plt_name +"'")}
${IF(LEN(own_org_prop_province) = 0, "", "AND cops.org_prop_province IN ('" + own_org_prop_province + "')")}
${IF(LEN(own_org_prop_city) = 0, "", "AND cops.org_prop_city IN ('" + own_org_prop_city + "')")}
${IF(own_org_status = "全部", "", IF(own_org_status = "自营", "AND org_id IN (o2)", IF(own_org_status = "加盟", "AND cops.org_id IN (o3)" ,"")))}
${IF(LEN(own_org_name) = 0, "", "AND cops.org_id IN ('" + own_org_name + "')")}
${IF(LEN(own_pdt_hier_catcode1) = 0, "", "AND cops.pdt_hier_catcode1 IN ('" + own_pdt_hier_catcode1 + "')")}
${IF(LEN(own_pdt_hier_catcode2) = 0, "", "AND cops.pdt_hier_catcode2 IN ('" + own_pdt_hier_catcode2 + "')")}
${IF(LEN(own_pdt_hier_catcode3) = 0, "", "AND cops.pdt_hier_catcode3 IN ('" + own_pdt_hier_catcode3 + "')")}
${IF(LEN(own_pdt_prop_factory_brand) = 0, "", "AND cops.fd_product_prop14 IN ('" + own_pdt_prop_factory_brand + "')")}
${IF(own_pdt_prop_dtp = "全部", "", IF(own_pdt_prop_dtp = "DTP", "AND fd_category_name4 = '新特药'", IF(own_pdt_prop_dtp = "其它", "AND fd_category_name4 != '新特药'" ,"")))}
${IF(own_pdt_prop_pr = "全部", "", IF(own_pdt_prop_pr = 'PR商品', "AND cops.pdt_prop_key IN ('PP+', 'PP' ,'P' ,'R')" , "AND cops.cops.pdt_prop_key ='" + own_pdt_prop_pr + "'"))}
${IF(LEN(own_pdt_prop_brand) = 0, "", "AND cops.fd_product_prop17 IN ('" + own_pdt_prop_brand + "')")}
${IF(LEN(own_fd_product_prop16) = 0, "", "AND cops.fd_product_prop16 IN ('" + own_fd_product_prop16 + "')")}
${IF(LEN(own_pdt_name) = 0, "", "AND cops.pdt_code IN ('" + own_pdt_name + "')")}
AND plt_name IN (SELECT fd_platform
FROM finedb.fine_roles_platform WHERE fd_role_name IN (${"'" + replace(fine_role,",","','") + "'"}))
AND org_hier_operation5 IN (SELECT fd_store_lev5 FROM finedb.fine_roles_authority_qyt WHERE fd_role_name IN (${"'"+replace(fine_role,",","','")+"'"}))
GROUP BY
cops.pdt_hier_catcode3
,cops.fd_product_prop14
,cops.fd_product_prop16
,DTP
,cops.pdt_prop_key
,cops.pdt_code
,cops.pdt_barcode
,cops.pdt_name