ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

帆软报表下拉框选项关联数据集:动态联动、权限过滤与SQL下推实践

帆软报表下拉框选项关联数据集:动态联动、权限过滤与SQL下推实践 1. 先把下拉框选项的来源想明白1.1 从一次“选项写死”的翻车说起帆软报表里做下拉框最容易踩的坑不是控件不会用而是一开始就把它做成了死的。我见过太多报表参数面板上一个“区域”下拉框选项是设计的时候手工敲进去的十几行文本当时开发者想的是“反正区域就这几个写死多省事”。结果半年后业务调整新增了两个大区数据仓库里 dim 表早就更新了报表上的下拉框还是老样子——用户在参数面板上根本选不到新区域导出来的数据跟业务系统对不上最后追责追到报表这儿。这件事的本质不是“懒”而是没有把下拉框选项当成一份会变化的数据来看待。只要选项背后是业务数据它就应该有自己的数据来源而帆软报表里最自然的载体就是数据集。所谓“自定义下拉框选项并关联至数据集代码”说的其实就是两件事一是选项不再靠手敲二是选项的过滤、排序、联动逻辑全部写进数据集的 SQL 或公式里让报表自己去找数据而不是靠人去维护列表。这篇内容适合三类人看刚接手帆软报表、参数面板还只会拖控件的同学手上有一堆报表要维护、被“选项对不上”折磨过的同学以及要把报表交给运维、希望后面改选项不需要动模板设计的同学。不管你用的是哪个版本思路是一致的只是界面字段位置略有差异。1.2 三种选项来源的对比别一上来就选最复杂的帆软下拉框控件的选项来源从机制上大致是三条路我按实际项目里的使用频率排一下。第一种是自定义字典就是在控件属性里手工填。它的优势是零依赖、加载最快适合“是与否”“启用与停用”“一至十二级”这类几乎不会变、也不需要跟数据库对齐的枚举。它的上限也很明显一旦要跟数据库表的主键对上或者要按用户权限过滤手工维护就会开始出错。第二种是数据库表直接选一张表指定哪个字段是实际值、哪个字段是显示值。它比自定义省事改表就生效但它只能绑定单表做不了复杂的过滤和关联也没法根据上级参数动态变化。我一般只在选项表结构特别规整、且不需要联动的场景用它。第三种是数据查询绑定数据集先建一个数据集把该过滤、该排序、该关联的逻辑全写在 SQL 里然后下拉框的数据字典指向这个数据集。这是真正意义上的“关联至数据集代码”也是联动、权限过滤、大数据量场景唯一靠谱的方案。选项来源适用场景改选项要动哪里支持参数联动大数据量表现自定义字典固定枚举、是/否改模板设计不支持除非走公式最好纯前端数据库表单表规整字典改数据库不支持一般全表拉取数据查询数据集动态选项、联动、权限过滤改数据集 SQL支持可控能下推条件自定义数据集函数选项结构特殊、需拼装改公式支持较差全量拉取后处理表格里最后一行单独说一句。帆软支持在自定义字典里写数据集函数比如用ds1.select(字段名)直接把某个数据集的一整列取出来当选项。这条路在选项需要二次拼装比如要把两个字段拼成显示文本时很好用但它是把整个数据集的结果拉到前端再处理数据量一大就会明显卡顿。所以我的习惯是能用 SQL 解决的就别用公式解决。1.3 什么情况下必须把选项挂到数据集上有几个信号一出现就说明这件事不能再手工维护了。一是选项之间存在层级关系。省份选完要筛城市城市选完要筛区县这种从属关系只能靠上级参数去过滤下级数据集手工列表做不到。二是选项要按登录人过滤。比如华南区的销售只能看到自己辖区的门店选项本身就得带上权限条件这必须在 SQL 里通过用户参数去拼。三是选项数量会持续增长。门店、经销商、物料、项目编号这类数据每个月都在变让人去改模板设计是不现实的。四是显示值和实际值不一致。用户看到的是“华东大区”传给数据集查询条件的是HD这中间必须有个映射关系手工维护容易错位。五是选项需要带排序、启用状态、备注。真实的选项表里通常有sort_no、is_active这些字段手工列表没法承载。只要命中上面任意一条就走“数据集 控件数据字典绑定”这条路别犹豫。2. 数据侧准备把选项表结构设计到位2.1 一张合格的选项表长什么样我一般会建议客户单独建一张字典表而不是直接从业务主表里distinct出选项。原因是主表随时可能被业务写入脏数据或者存在停用但历史数据仍在的记录直接从主表取选项下拉框里会冒出一堆不该出现的东西。下面这张表是我在多个项目里反复用过的结构字段不多但每个都有存在的理由。CREATE TABLE dim_option ( opt_id VARCHAR(32) NOT NULL, -- 实际值传给数据集做查询条件 opt_text VARCHAR(100) NOT NULL, -- 显示值用户在下拉框里看到的 opt_type VARCHAR(32) NOT NULL, -- 选项类型如 area/store/product parent_id VARCHAR(32) DEFAULT NULL, -- 上级选项的实际值用于联动 sort_no INT DEFAULT 0, -- 排序号越小越靠前 is_active TINYINT DEFAULT 1, -- 是否启用0 表示不再展示 ext_remark VARCHAR(200) DEFAULT NULL, -- 备注便于运维排查 update_time DATETIME DEFAULT NULL, PRIMARY KEY (opt_id, opt_type) );几点设计上的说明。opt_id和opt_type做成联合主键是因为同一张表可能要放多种类型的选项用opt_type做区分比建好几张表更好维护parent_id存的是上级的opt_id这样任意层级都能用同一套逻辑处理不用为二级、三级分别建表sort_no必须有数据库默认返回顺序是不可靠的不加排序字段下拉框的顺序会随着数据变动而抖动用户体验很差is_active用于软删除历史报表数据还要引用这个选项物理删除会破坏历史数据的可读性。2.2 数据集 SQL 里怎么处理参数和空值这是整篇内容里最容易被忽略、但出错率最高的地方。下拉框绑定的数据集往往需要根据上级参数过滤而上级参数在第一次加载时是空的——如果不处理空值SQL 就会变成AND parent_id 结果一条都查不出来。帆软数据集里引用参数的写法是${参数名}配合if()函数做条件拼接。下面这段是我常用的写法SELECT opt_text AS show_text, opt_id AS real_value FROM dim_option WHERE opt_type city AND is_active 1 ${if(len(p_province) 0, , AND parent_id p_province )} ORDER BY sort_no ASC, opt_id ASC逐个拆开讲。opt_text和opt_id起了别名是因为后面在控件里选字段时名字越清晰越不容易选错。is_active 1保证停用的选项不会出现在下拉框里。最关键的是第三行len(p_province) 0判断上级参数是否为空为空时拼一个空字符串也就是不加任何过滤条件这样首次加载能展示全部选项不为空时才拼上过滤条件。注意if()里拼接字符串时帆软的公式语法用双引号表示字符串常量而 SQL 里的字符串字面量要用单引号所以写成AND parent_id p_province 。单双引号写反了SQL 直接报语法错误。还有一个细节p_province是上级下拉框的参数名它必须和数据集里的${}引用名完全一致大小写敏感。我见过很多次“联动不生效”的问题最后发现是控件名写成了province数据集里引用的是p_province这种低级错误排查起来相当费时间。2.3 内置数据集、服务器数据集与视图的取舍选项数据量小的时候几百行以内其实没必要每次都连库查。帆软支持内置数据集直接把数据写进模板里也支持服务器数据集挂在服务器上多个模板共用。我的取舍经验是这样的全公司共用的基础字典组织架构、区域、产品线做成服务器数据集好处是改一次全部报表生效不用逐个模板改模板专属的选项某张报表特有的统计口径枚举用模板数据集避免污染全局纯静态且几乎不变的选项用内置数据集或者干脆用自定义字典省掉一次数据库往返。用视图也是一种选择。如果选项的过滤逻辑比较复杂多表关联、大量 CASE WHEN在数据库里建好视图然后帆软数据集直接SELECT * FROM v_option_xxx好处是 SQL 逻辑收在数据库侧统一维护坏处是视图一旦改动所有引用它的报表都会受影响需要考虑兼容性。我个人偏向于把逻辑放在帆软数据集里因为改起来不需要动数据库权限报表开发同学自己就能搞定。3. 把下拉框控件真正接到数据集上3.1 数据字典绑定的两种操作路径数据集建好之后回到参数面板选中下拉框控件打开控件属性找到“数据字典”这一项。这里有两个入口。第一个入口是数据字典类型选“数据查询”然后在下拉里选中你刚才建的数据集紧接着会出现两个字段选择框一个对应实际值一个对应显示值。这条路最直观我推荐作为默认方案。需要注意的是只有数据集里返回的字段才会出现在候选里如果字段是拼出来的别名记得别名要起得能认出来。第二个入口是自定义 数据集函数。数据字典类型选“自定义”部分版本会提供一个“使用公式”的开关打开之后可以写数据集函数例如ds_city.select(show_text)表示把ds_city这个数据集里show_text这一列的所有值作为显示值。如果要加条件可以写成ds_city.select(show_text, parent_id $p_province)这种形式第二个参数是过滤条件表达式。具体怎么选我给自己定的规则是只要能从数据集里直接选出两列实际值、显示值就用“数据查询”只有当显示文本需要拼装比如“门店编码 - 门店名称”这种或者选项需要去重、需要跨数据集组合时才用自定义加公式。原因是公式方式会把数据集结果完整加载到前端性能和内存占用都不如第一种可控。提示不同大版本里自定义数据字典的填写格式略有差异通常是每行一个选项、用英文逗号分隔两列。填好之后一定要点一下界面上的“预览”看实际渲染结果别凭记忆猜哪列在前。我至少遇到过三次因为两列顺序反了用户看到的是编码、传出去的是名称报表数据全错。3.2 实际值和显示值这对关系必须刻在脑子里下拉框有两个身份给用户看的“显示值”和传给参数、传进 SQL 的“实际值”。用户在下拉框里看到“华东大区”但参数p_area的值是HD最终进 SQL 的是AND area_code HD。这两个东西混了症状通常很隐蔽——报表能出数据但数据不对或者导出的时候显示成编码。常见的翻车场景有三个。第一个是显示值选了编码列用户一脸茫然看着一串数字。第二个是实际值和数据库字段类型不一致比如实际值存的是001数据库字段是整型1WHERE id 001在部分数据库里匹配不上。第三个是多选场景下控件传出来的是数组而 SQL 里按单个值处理导致只有第一个值生效。我的做法很朴素建数据集的时候就把两列的别名固定成real_value和show_text控件里一律按这个命名去选减少误选概率。数据库字段类型是数值的选项表里也存成数值类型不要用字符串硬凑从源头避免类型不匹配。3.3 控件属性里几个容易被忽略的设置数据字典绑完之后控件属性页里还有一串开关用好了能省很多事用错了会出莫名其妙的 bug。默认值这一项可以让报表一打开就有个合理的选择而不是空着等用户选。默认值可以填固定值也可以填公式比如用sql()函数从库里取第一条sql(ds_city, SELECT MIN(opt_id) FROM dim_option WHERE opt_typecity AND is_active1, 1, 1)。我的习惯是只给必填的、且业务上有明确默认的控件设默认值其余留空否则用户不知道系统替他选了什么。允许多选勾上之后参数值会变成数组SQL 里的写法要跟着改下一节细说。这里先记住一个坑多选下拉框在数据集里如果用去比较只会命中第一个值。允许编辑这个开关要慎用。打开之后用户可以直接在下拉框里输入文本如果这个文本不在选项里参数就会带着一个库里查不到的值传下去结果是查询空白。除非业务明确要求模糊搜索否则我会关掉它改用选项过滤来实现搜索——选项过滤是在已有选项里做前端筛选值一定是合法的。选项过滤在选项数量大上千条时体验会下降因为它是在前端已有的数据集结果上做匹配。真正大数量的场景要用“搜索型下拉”或者把过滤条件下推到 SQL这个在后面性能部分展开。4. 联动与动态选项的实际做法4.1 省市区三级联动关键在数据集而不在控件联动这件事很多人以为要在控件上配置一堆关系其实核心全在数据集里。控件的联动设置只负责一件事上级控件值变化时通知下级控件刷新。标准做法是三个下拉框参数名分别是p_province、p_city、p_district。省级下拉框绑定的数据集不带任何参数过滤市级下拉框绑定的数据集里写了${if(len(p_province)0,,AND parent_idp_province)}区县同理依赖p_city。然后在省级控件的属性里找到“联动”或者“绑定其他控件”的设置项把市级、区县两个控件勾上表示值变化时刷新它们。市级控件再联动区县。刷新本质上就是让下级控件重新执行一次数据字典的数据集查询。-- 市级数据集 SELECT opt_text AS show_text, opt_id AS real_value FROM dim_option WHERE opt_type city AND is_active 1 ${if(len(p_province) 0, , AND parent_id p_province )} ORDER BY sort_no -- 区县数据集 SELECT opt_text AS show_text, opt_id AS real_value FROM dim_option WHERE opt_type district AND is_active 1 ${if(len(p_city) 0, , AND parent_id p_city )} ORDER BY sort_no实操中有个高频问题改了省级选项市级下拉框还显示着上一次选的城市但那个城市已经不属于新省份了于是查询条件里省和市不匹配结果为空。解决办法是在市级控件的默认值里不要写死固定值或者写一个依赖上级参数的公式默认值让它刷新后自动落到合法的第一项。4.2 多选参数进 SQL 的正确姿势勾了“允许多选”之后参数值是一个数组。帆软在数据集里展开数组时会自动把每个元素加上引号并用逗号连接所以 SQL 里直接写IN就行不需要自己拆。SELECT store_name, store_code FROM dim_store WHERE is_active 1 ${if(len(p_area) 0, , AND area_code IN ( p_area ))}这里有个容易踩的点p_area展开后是HD,HN这种形式如果数据库字段是数值型加引号之后在部分数据库上会触发隐式转换索引可能失效数量大时性能会掉。所以选项的实际值字段类型最好和业务表字段类型保持一致。另一个点是空值判断。多选控件如果用户一个都没选参数是长度为 0 的数组len(p_area) 0依然成立不会拼出IN ()这种语法错误。但如果用户手工输入了一个不存在的值就需要靠前面提到的“允许编辑”开关去堵住。4.3 选项几千上万条时怎么不卡选项数量上去之后卡顿主要来自两个地方数据集每次刷新都全量查一遍以及前端渲染几千个option。针对第一个问题思路是把过滤条件下推到 SQL。不要指望“先把全部选项查出来再在前端过滤”那是必然要卡的。上级参数能过滤掉大部分数据就一定要过滤。如果确实需要一次性展示大量选项比如无上级依赖的产品字典可以考虑加数据库索引把opt_type和is_active做成联合索引。针对第二个问题帆软的“选项过滤”只是前端匹配数据量大时首次渲染就要花时间。我的经验是超过两三千条选项就别用普通下拉框了改用支持搜索输入的控件形态并且把搜索关键字作为参数传到 SQL 里用LIKE过滤让前端每次只拿到几十条。这样不管总量有多少渲染压力都恒定。还有一点常被忽略数据字典的数据集是有缓存的。改了 SQL 之后如果不清理缓存下拉框可能还展示旧数据会让人误以为改错了地方。改完记得刷新模板或者清一下缓存再验证。5. 常见问题速查与排查思路5.1 下拉框空白或选项不全这类问题按下面的顺序排查基本能覆盖九成情况。现象可能原因排查动作下拉框完全空白数据集本身查不出数据单独执行数据集 SQL确认有返回行下拉框完全空白实际值/显示值字段选错检查控件里选的字段名是否与数据集别名一致只显示一部分SQL 里有is_active1等条件去掉条件执行确认是不是被过滤掉了首次加载有数据选完上级后空白空值处理缺失拼出了 检查if(len(...)0,...)写法显示的是编码不是名称显示值字段选成了实际值列在数据集里核对两列别名选项顺序随机跳动SQL 没有ORDER BY加排序字段必要时再加一个稳定的次排序字段数据集本身执行正常但控件没数据还有一个隐蔽原因数据集定义在模板上而控件引用的数据集被删掉或改名了控件里的引用变成了失效状态。这种情况在多人协作改模板时时有发生检查方式是把控件的数据字典重新选一遍数据集。5.2 选了值但报表查不出数据这是第二大类问题本质是参数没传对或者传进去的值不匹配。先看参数名。控件名和数据集里${}引用的名字必须完全一致。帆软里参数名是大小写敏感的p_area和P_AREA会被当成两个东西。再看值的类型。如果选项表里opt_id是字符串001而业务表里存储的是数值1WHERE id 001在某些数据库上不会报错但匹配不到表现就是查不出数据。这种问题最坑因为它不报错。解决办法是统一类型或者在 SQL 里显式转换。还要看多选。如果控件勾了多选但 SQL 里写的是只会取数组的第一个元素用户选了三个区域结果只查了一个。改成IN即可。最后看是不是有隐藏的前后空格。选项表里的值如果是从 Excel 导入的尾部带空格很常见HD 和HD不相等。这个用TRIM()在数据集里处理掉最省事。5.3 导出、打印和定时任务里的表现差异参数面板上的下拉框在页面上工作正常不代表导出和定时任务也正常。原因在于导出和定时任务执行时参数需要由程序或配置提供控件本身不参与交互。如果导出时下拉框相关的内容显示为空通常是因为导出时参数没有默认值导致数据集条件拼成了空。这时候给控件配一个合理的默认值就很关键或者在报表的导出配置里显式指定参数值。定时任务场景下参数只能通过任务配置或者调度参数来传。如果报表依赖某个下拉框参数做过滤而任务里没配这个参数就会走空值分支查出来的可能是全量数据。我的做法是把这类报表的参数默认值设计成“全量”语义即空值代表不过滤然后在任务配置里明确写上需要的过滤值避免误发全量数据。另外如果你在控件上用了前端 JS 去动态设置选项导出和定时任务里这段 JS 是不执行的选项会退回到数据字典的原始结果。所以凡是影响数据正确性的逻辑都要放在 SQL 或数据集公式里别放在前端脚本里。6. 走一遍完整流程顺便说说踩过的坑6.1 从零到能用的操作清单假设要做一张门店销售报表参数是区域和门店两级联动选项全部来自数据库。第一步建选项表dim_option把区域和门店都灌进去区域记录的parent_id为空门店记录的parent_id指向所属区域。第二步在帆软里建两个模板数据集。区域数据集不带参数只按opt_typearea过滤门店数据集带上${if(len(p_area)0,,AND parent_idp_area)}。第三步在参数面板拖两个下拉框控件分别命名为p_area和p_store注意名字要和数据集里的参数名一字不差。第四步给区域控件绑定区域数据集实际值选real_value显示值选show_text门店控件同理绑定门店数据集。第五步在区域控件的属性里设置联动门店控件这样区域一变门店选项自动刷新。第六步把报表主体数据集的 SQL 也接上参数写法跟前面多选那一节一致注意空值处理。第七步验证三种情况两个参数都为空时是否显示全量只选区域时门店是否跟着变选完门店后数据是否正确。6.2 几个只有做过才懂的细节关于选项表的脏数据我的建议是加一个数据质量检查的定时任务定期扫一遍有没有parent_id指向了不存在的记录或者is_active0但仍有引用的情况。这种问题不会立刻暴露等报表用的人多了才会有人反馈“选项点进去没数据”。关于默认值的取舍我个人的偏好是能不设默认值就不设让用户主动选择这样数据口径最清楚。只有在定时任务、批量导出这种无人交互的场景才配默认值并且默认值的语义必须是业务确认过的。关于数据集命名千万别用ds1、ds2这种。一张报表里数据集多了之后别人接手维护时根本分不清哪个是给控件用的、哪个是给报表主体用的。我的命名习惯是ds_param_area、ds_param_store、ds_main_sales一眼能看出用途排查问题时省下的时间远超当初多敲的那几个字符。关于选项过滤和模糊搜索的边界需要提醒的是选项过滤只能匹配已有选项如果用户想按门店名称里的某个字去搜而显示文本里正好包含这个字是可以搜到的但如果用户想按门店编码搜索而编码不作为显示值那就搜不到。这种需求要么把编码拼进显示文本要么把搜索关键字作为参数下推到 SQL 里做LIKE两者选一个别指望前端过滤能解决所有搜索需求。最后说一个我在实际维护中最有体会的点把选项逻辑全部收进数据集的那一刻报表的维护成本就下来了。以前改一个区域选项要打开模板、找控件、改文本、重新发布现在只需要在数据库里改一行记录或者在选项表里加一条报表刷新之后自动生效。这条路径唯一的代价是前期要把表结构和 SQL 写扎实而这份投入在报表生命周期的第二次需求变更时就已经回本了。
返回列表