ARTICLE DETAIL

资讯详情

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

泛微OA流程表单归档与未归档SQL联表查询详解

泛微OA流程表单归档与未归档SQL联表查询详解 在泛微OA里做数据查询最容易被卡住的就是“归档”这个概念。我接手公司OA维护的第一周就有人让我拉一份去年的报销流程明细我自信满满地打开SQL Server找到那个名字里带formtable的业务表一顿查询操作猛如虎结果只出到上个月的数据再往前的记录一条都没有。当时我还以为是权限问题后来才明白是掉进了“归档与未归档”的坑里。这个问题的根源在于泛微E-cology的表结构把“业务数据”和“流程状态”分开存放而归档动作会同时影响这两类表。搞不清楚这个机制写出来的联表查询不是数据缺一半就是把归档记录和未归档记录混在一起报表根本没法用。这篇文章就把泛微OA流程表单归档与未归档的SQL联表查询讲透内容包括表结构、归档判断字段、三种标准查询写法以及我在实际排查中踩过的坑。适合OA系统管理员、报表开发工程师以及刚接手泛微数据库但还没完全摸清表关系的朋友。1. 泛微OA里“归档”到底改了什么先看清业务语义再写SQL1.1 归档不是“删数据”也不是简单“换表”很多第一次接触泛微数据库的人会有一个直觉归档就是把数据从业务表挪到另一张“归档表”里未归档数据留在原表。这个直觉只对了一半而且不同版本的泛微处理方式差异很大。在E-cology 8/9系列中流程表单的数据确实存放在formtable_表单ID这样的业务主表里每一条业务数据对应一个requestid这个requestid就是该数据在流程引擎中的唯一标识。流程从发起、审批、直到归档这条业务记录始终还在formtable表里并不会真的消失。归档动作真正改变的是两个地方一是流程请求主表workflow_requestbase中的状态字段常见的是currentnodetype流程归档后这个字段会变成已归档对应的值二是部分系统配置了独立的历史归档表会把超过一定时间的旧数据复制到带archive标记的表中然后把原表数据清理掉。所以你在查泛微数据时第一步要判断的不是“怎么写SQL”而是“我这套系统的归档机制到底是哪一种”。判断方法其实很简单在数据库中执行这样一条SQL看看当前库里有哪些表SELECT name FROM sys.tables WHERE name LIKE %formtable_% OR name LIKE %archive% ORDER BY name;如果发现既有formtable_1001又有archived_formtable_1001这类表说明系统开启过历史归档迁移如果只有formtable_开头的表没有独立归档表那归档只是workflow_requestbase里的一个状态标记数据仍然全部躺在原表中。1.2 三种归档状态口径别只认一个字段在撰写联表查询之前还需要明确“归档”的判断口径。我发现很多教程会直接告诉你“currentnodetype3就是已归档”但这个结论在不同版本的泛微OA中是会变形的。我常用的判断口径有三套查询时往往要组合起来用。第一套是流程状态口径也就是看workflow_requestbase.currentnodetype。在大多数E-cology版本中这个字段为3时代表流程已归档。它的优点是直观字段值稳定适合作为归档统计的主要依据。第二套是待办残留口径看workflow_currentoperator表。这张表记录当前有哪些节点还有待办操作人如果一个流程已经走完所有审批节点这张表里就没有未完成的记录。反过来只要这张表里还存在未完成的待办就可以判定流程还没结束。这套口径不依赖于归档状态字段适合用来判断“流程是否实际办结”。第三套是物理存储口径看数据到底在formtable原表还是在archived_formtable归档表里。这套口径最可靠但只有系统开了归档迁移策略时才适用。写SQL时我会先用第二套或第三套口径验证一下第一套口径的准确性避免因为版本差异导致统计结果失真。2. 流程表单查询绕不开的几张核心表结构与关联关系2.1 业务数据表与流程表怎么关联要写联表查询先得知道有几张表参与关联。我整理了一张速查表按泛微E-cology 8/9版本为例不同版本可能略有差异但核心表的逻辑是相通的表名作用核心字段备注workflow_requestbase流程请求主表每条流程实例一条记录requestid,workflowid,requestname,creater,createdate,currentnodeid,currentnodetype判断归档状态的主要来源formtable_表单ID建模表单业务数据主表requestid, 各类业务字段表单ID对应表单建模时的IDformbizmain_表单ID流程表单关联中间表requestid,billid业务表与流程表之间的桥workflow_currentoperator当前待办操作人表requestid,nodeid,userid,iscompleted判断流程是否还有未完成待办hrmresource人员表id,lastname,departmentid关联申请人姓名hrmdepartment部门表id,departmentname关联申请人部门workflow_requestlog流程操作记录表requestid,nodeid,operateuserid,operatedate查询节点处理痕迹最常见的关联关系是formtable_1001.requestid workflow_requestbase.requestid这是核心关联条件几乎所有流程表单查询都绕不开它。formbizmain_表单ID这张表很多人容易忽略。它的价值在于处理表单与流程之间的多对多绑定关系比如一个表单可能被多个流程复用或者一个流程绑定多张表单时光靠requestid关联会把数据查重。遇到这种情况通过formbizmain中间表明确当前查询的是哪条流程绑定关系结果会更准确。人员关联也要注意workflow_requestbase.creater存的是发起人的id不是姓名。要显示发起人姓名需要LEFT JOIN hrmresource再关联hrmdepartment取部门名称。这里建议用LEFT JOIN而不是INNER JOIN原因很简单人员信息可能被删除或停用用INNER JOIN会导致整条记录消失报表里莫名少数据。2.2 判断当前系统是否有独立归档表的排查SQL写查询前先用下面SQL确认这个系统里所有“看起来像归档表”的对象以及字段结构SELECT t.name AS table_name, c.name AS column_name, ty.name AS data_type FROM sys.tables t LEFT JOIN sys.columns c ON t.object_id c.object_id LEFT JOIN sys.types ty ON c.user_type_id ty.user_type_id WHERE t.name LIKE %formtable% OR t.name LIKE %archive% OR t.name LIKE %archived% ORDER BY t.name, c.column_id;执行完以后重点看两件事有没有带archive前缀或后缀的表如果有这些表和原表字段是否一致。字段不一致时联表查询里就必须对字段做补齐处理否则UNION ALL会直接报列数不对应的错误。3. 归档与未归档联表查询的三种标准写法3.1 写法一主表数据未迁移用流程状态字段区分这是最常用、也最简单的情况。系统没有做历史归档迁移所有数据都还在formtable_1001主表里区分归档和未归档只需要在workflow_requestbase上加条件判断。假设表单ID为1001业务字段有field1摘要、field2金额现在需要查所有已归档的报销流程附带申请人和部门SELECT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 申请人, dept.departmentname AS 部门, f.field1 AS 摘要, f.field2 AS 金额, CASE WHEN rb.currentnodetype 3 THEN 已归档 ELSE 未归档 END AS 归档状态 FROM formtable_1001 f INNER JOIN workflow_requestbase rb ON f.requestid rb.requestid LEFT JOIN hrmresource emp ON rb.creater emp.id LEFT JOIN hrmdepartment dept ON emp.departmentid dept.id WHERE rb.currentnodetype 3 AND rb.createdate 2024-01-01 ORDER BY rb.createdate DESC;这段SQL的关键点是rb.currentnodetype 3这个条件。有些环境里归档状态的字段值可能不是3而是2或者其他数字第一次写这个条件前最好先用下面这条SQL把字段值的分布跑一遍确认3确实代表已归档SELECT currentnodetype, COUNT(*) AS cnt FROM workflow_requestbase GROUP BY currentnodetype ORDER BY currentnodetype;看到各个取值的记录数分布后再结合实际OA界面上已知的归档数据量就能反推哪个值代表归档。这种验证方式比直接搜配置文档更靠谱因为文档描述的是标准安装环境而实际系统可能被实施方改过。3.2 写法二存在独立归档表用UNION ALL合并如果排查后发现系统真的存在archived_formtable_1001这样的归档表那就不能只查一张表了。归档前的旧数据可能已经不在formtable_1001里而是被搬到了archived_formtable_1001中。这时候的正确写法是用UNION ALL把两张表的数据合并起来然后整体标记归档状态:SELECT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 申请人, dept.departmentname AS 部门, f.field1 AS 摘要, f.field2 AS 金额, 已归档 AS 归档状态 FROM archived_formtable_1001 f INNER JOIN workflow_requestbase rb ON f.requestid rb.requestid LEFT JOIN hrmresource emp ON rb.creater emp.id LEFT JOIN hrmdepartment dept ON emp.departmentid dept.id UNION ALL SELECT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 申请人, dept.departmentname AS 部门, f.field1 AS 摘要, f.field2 AS 金额, 未归档 AS 归档状态 FROM formtable_1001 f INNER JOIN workflow_requestbase rb ON f.requestid rb.requestid LEFT JOIN hrmresource emp ON rb.creater emp.id LEFT JOIN hrmdepartment dept ON emp.departmentid dept.id;这里有个非常容易出错的点两张表的字段顺序必须一致类型也要一致。如果归档表比原表少了字段比如archived_formtable_1001没有field2这个字段查询时就要用CAST(NULL AS DECIMAL(18,2)) AS field2补位否则UNION ALL报错。还有一点这里必须用UNION ALL千万不要图省事写成UNION。UNION会做去重如果两条业务记录恰好所有字段值都相同就会被合并成一条数据量直接变少。归档表和原表理论上不会出现完全重复的记录但用UNION ALL才是最稳妥的。3.3 写法三基于待办操作人反推未归档数据有些特殊场景下归档状态字段并不可靠比如流程已经办结但归档动作没触发或者相反。这时候可以用workflow_currentoperator来反推。凡是workflow_currentoperator表中还存在未完成待办的流程无论currentnodetype显示什么实际都还在流转中属于“未归档”范畴。查询未归档流程的SQL可以这样写SELECT DISTINCT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 当前待办人 FROM workflow_requestbase rb INNER JOIN workflow_currentoperator wco ON rb.requestid wco.requestid LEFT JOIN hrmresource emp ON wco.userid emp.id WHERE wco.iscompleted 0;注意DISTINCT不能省。一个流程可能有多个节点同时存在待办比如会签、或签场景下同一流程在workflow_currentoperator表里有多条未完成记录不加DISTINCT会出现重复的流程标题。这套写法的好处是不依赖具体字段值逻辑上更接近业务真实状态。缺点是只能判断“未归档”不能直接区分“已归档”和“已办结但未执行归档动作”适合做催办清单时使用。3.4 综合示例查已归档且金额大于5000的流程把上面的方法组合起来一个完整的查询需求是查找已归档报销流程中金额大于5000元、且申请部门是“财务部”的记录按发起时间倒序排列。假设没有独立归档表数据都在formtable_1001主表中SELECT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 申请人, dept.departmentname AS 部门, f.field1 AS 摘要, f.field2 AS 金额 FROM formtable_1001 f INNER JOIN workflow_requestbase rb ON f.requestid rb.requestid LEFT JOIN hrmresource emp ON rb.creater emp.id LEFT JOIN hrmdepartment dept ON emp.departmentid dept.id WHERE rb.currentnodetype 3 AND f.field2 5000 AND dept.departmentname N财务部 ORDER BY rb.createdate DESC;如果系统存在独立归档表只需要把前半部分改成UNION ALL两个分支每个分支各带同样的WHERE条件这里不再重复贴代码。4. 实际查询中我踩过的坑和排查思路4.1 统计结果少一半漏了UNION ALL这个坑我印象太深了。月初给管理层做流程效率报表查“上个月已归档流程总数”我直接查了formtable_1001和workflow_requestbase联表结果数量比OA系统里看到的少了将近一半。当时第一反应是权限问题排查了半天最后一个老实施顾问提了一句“你看看是不是有归档表”才恍然大悟。排查这类问题时最快的方法是做一个总数对比。分别执行以下三条SQL看数量差异SELECT COUNT(*) FROM formtable_1001; SELECT COUNT(*) FROM archived_formtable_1001; SELECT COUNT(*) FROM workflow_requestbase WHERE currentnodetype 3;如果归档表里有记录而主表里没有就说明系统确实做了归档迁移必须用UNION ALL。4.2 归档表字段和主表字段不一致比“有归档表”更折磨人的是“归档表有但字段对不上”。我遇到过的情况是主表formtable_1001有field1到field20共20个字段归档表archived_formtable_1001只有其中有值的8个字段其他字段被设计者认为“不需要保留”而没有迁移。这种情况下的处理原则是以归档表实际存在的字段为准缺失字段在UNION ALL时用默认值补齐。比如归档表缺少金额字段field2就写SELECT requestid, ..., CAST(NULL AS DECIMAL(18,2)) AS 金额, ... FROM archived_formtable_1001 UNION ALL SELECT requestid, ..., f.field2 AS 金额, ... FROM formtable_1001 f;这样能保证列数对齐报表侧也不用担心空值问题后续在视图或者报表工具里处理NULL值即可。4.3 SQL Server 2008 R2带来的语法兼容问题泛微老环境很多跑在SQL Server 2008 R2上这个版本的SQL语法支持有限。我在写查询时曾踩过几个具体坑TRIM函数在SQL Server 2017之前不存在老版本只能用LTRIM(RTRIM(字段))STRING_AGG是SQL Server 2017才有的老版本需要用FOR XML PATH实现字符串拼接CONCAT函数在2012才引入2008 R2要用拼接而且拼接时遇到NULL会导致整个结果变成NULL需要提前用ISNULL处理。如果只是自己查询问题不算大。但如果要把查询固化成视图或存储过程给团队其他人用就要特别注意这些兼容性问题。我现在的习惯是凡是跑在2008 R2上的查询统一用老语法写宁可不那么简洁也不要让同事在执行时报错。4.4 没确认数据一致性前不要直接UPDATE最后这个坑不是查询的问题而是从查询延伸到写操作时的教训。有段时间业务方让我“批量修正一批单据的申请人”理由是OA界面上显示的申请人不对。我写了一条UPDATE formtable_1001 SET creater xxx WHERE ...执行完后界面上申请人确实变了但流程日志里记录的操作人、审批链路上的历史记录还是旧的。业务方后来核对流程历史时发现了问题只能通过备份还原来恢复。泛微的数据表之间有非常强的关联约束。workflow_requestbase.creater是业务表里requestid对应流程的发起人workflow_requestlog和workflow_currentoperator又在不同节点记录了操作人。直接改业务表等于只改了一个维度其他维度的数据不会跟着变。所以在泛微里不要轻易用SQL直接修改表单主表或流程表的核心字段。遇到数据修正需求优先走OA系统的接口或后台功能实在要改也必须先全链路梳理清楚涉及哪些表做好备份并在测试库完整验证一遍。查询随便怎么写都行写操作一定要克制。4.5 日期字段类型不统一泛微里的日期字段类型很不统一。部分字段是datetime部分自定义字段可能是varchar存的是“2024-01-01 10:30:00”这种字符串。联表查询时一旦在主表字段上加了时间范围过滤而该字段是varchar类型就可能导致过滤结果不准或者查询性能下降。遇到这种情况建议先确认字段类型SELECT name, system_type_id, user_type_id FROM sys.columns WHERE object_id OBJECT_ID(formtable_1001) AND name IN (field1, field2, requestid);如果确认是varchar类型的日期字段查询时要显示转换为datetimeWHERE CONVERT(datetime, f.field_date, 120) 2024-01-01但这会让索引失效数据量大时性能会很难看。更好的办法是尽量使用workflow_requestbase.createdate这种系统自带的datetime字段做时间过滤业务字段只做展示和筛选。5. 进阶把归档查询做成可以长期用的报表视图5.1 归档率统计SQL做流程效率分析时经常要看某个流程模板的归档率也就是已归档流程数占全部流程数的比例。这个统计可以直接用分组汇总完成SELECT SUM(CASE WHEN rb.currentnodetype 3 THEN 1 ELSE 0 END) AS 已归档数, COUNT(*) AS 总流程数, CAST(SUM(CASE WHEN rb.currentnodetype 3 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS DECIMAL(5,2)) AS 归档率百分比 FROM workflow_requestbase rb INNER JOIN formtable_1001 f ON rb.requestid f.requestid WHERE rb.workflowid 123;如果系统有独立归档表统计口径要改成“主表未归档数 归档表已归档数”否则会漏掉迁走的旧数据。5.2 查找超过30天未归档的僵尸流程业务部门经常让我查“有哪些流程发起很久了还没归档”用于催办或清理。我的做法是先用NOT EXISTS关联workflow_currentoperator判断是否还有未完成待办再叠加时间条件SELECT rb.requestid, rb.requestname AS 流程标题, rb.createdate AS 发起日期, emp.lastname AS 发起人 FROM workflow_requestbase rb LEFT JOIN hrmresource emp ON rb.creater emp.id WHERE rb.currentnodetype 3 AND rb.createdate DATEADD(DAY, -30, GETDATE()) AND NOT EXISTS ( SELECT 1 FROM workflow_currentoperator wco WHERE wco.requestid rb.requestid AND wco.iscompleted 0 ) ORDER BY rb.createdate;这套查询有个很实用的点30天的阈值可以通过DATEADD(DAY, -N, GETDATE())灵活调整业务方说要查“超过一周没归档”就把-30改成-7不用改其他逻辑。5.3 把查询固化成视图如果这个归档联表查询每周都要用我会建议直接在数据库里创建视图这样以后查询只需要SELECT * FROM v_workflow_form_archive不用每次都写一大段联表SQL。视图的核心逻辑就是把前面“写法二”的UNION ALL固化下来。需要注意几点视图里不要写ORDER BY排序交给查询视图的人视图字段用中文字段别名时建议统一在视图定义里设置好避免每个使用者在查询时才加别名视图建好后在测试库先跑一遍确认执行计划中没有明显的全表扫描再放上线。如果数据量特别大还可以考虑在formtable表的requestid和workflow_requestbase表的requestid上确认是否有索引。没有索引的话联表查询性能会随数据量增长急剧下降。我接手泛微数据库这几年最大的体会是写查询之前先搞清楚你这套系统到底开没开归档迁移、归档字段的值代表什么、有没有中间关联表。不同版本、不同实施方部署的表结构差异很大网上看到的任何SQL都不能直接照搬必须先在测试库把表结构和字段取值摸一遍再动手写正式查询。把这套流程跑顺以后归档与未归档的联表查询就没有想象中那么复杂了。
返回列表