Asp.net 6 MVC中Oracle存储过程返回JSON对象时DISTINCT失效导致WBS重复的问题求助
嘿,我看你在Asp.net 6 MVC环境下调用这个Oracle存储过程时,遇到了DISTINCT失效、WBS字段重复的麻烦。先帮你拆解下问题根源,再给几个实用的解决思路:
问题根源分析
你现在CTE里的DISTINCT L.WBS_LEVEL_ID其实没起到实际作用——这个CTE只是单独提取了唯一的WBS_LEVEL_ID,但后续的JOIN操作(尤其是和BASELINE_RPT B、BCP BC的关联)会把数据扩展开:如果一个WBS对应多个基线版本(B.REV_NUMBER不同),或者一个基线对应多条BCP记录,最终结果就会出现多条相同WBS的记录,CTE的DISTINCT根本管不到后续关联后的重复。
另外注意到你的存储过程里没用到输入参数in_WBS_LEVEL_ID、in_FISCAL_YEAR、in_FISCAL_MONTH,这可能也是导致数据冗余、重复增多的原因,记得加上参数过滤!
可行的解决思路
思路1:用窗口函数筛选每个WBS的最新版本
从你最后按B.REV_NUMBER DESC排序的逻辑来看,推测你大概率只需要每个WBS的最新基线版本。这种情况下,用ROW_NUMBER()窗口函数给每个WBS的记录按版本号倒序排序,只保留行号为1的记录就能精准去重:
修改后的存储过程代码示例:
PROCEDURE GET_BASELINE_RPT (in_WBS_LEVEL_ID IN NUMBER, in_FISCAL_YEAR IN VARCHAR2, in_FISCAL_MONTH IN VARCHAR2, RET OUT CLOB) AS BEGIN SELECT JSON_ARRAYAGG ( JSON_OBJECT ( 'WBS' VALUE t.WBS_LEVEL_NAME, 'Title' VALUE t.DESCRIPTION, 'Rev' VALUE t.REV_NUMBER, 'ScopeStatus' VALUE t.STATUS, 'BCP' VALUE CASE WHEN t.FISCAL_YEAR = 0 THEN '' ELSE SUBSTR(t.FISCAL_YEAR,3,2)||'-'||LPAD(t.BCP_FISCAL_ID, 3, '0') END, 'BCPApprovalDate' VALUE t.APPROVAL_DATE, 'Manager' VALUE t.NICK_NAME, 'ProjectControlManager' VALUE t.NICK_NAME_2, 'ProjectControlEngineer' VALUE t.NICK_NAME_3, 'FiscalYear' VALUE t.FISCAL_YEAR_W, 'FiscalMonth' VALUE t.FISCAL_MONTH_W, 'WBSNumber' VALUE t.WBS_LEVEL_ID ) RETURNING CLOB ) INTO RET FROM ( SELECT L.WBS_LEVEL_NAME, W.DESCRIPTION, B.REV_NUMBER, W.STATUS, BC.FISCAL_YEAR, BC.BCP_FISCAL_ID, BC.APPROVAL_DATE, P1.NICK_NAME, P2.NICK_NAME AS NICK_NAME_2, P3.NICK_NAME AS NICK_NAME_3, W.FISCAL_YEAR AS FISCAL_YEAR_W, W.FISCAL_MONTH AS FISCAL_MONTH_W, L.WBS_LEVEL_ID, -- 按WBS分组,版本号倒序,标记最新版本为行号1 ROW_NUMBER() OVER (PARTITION BY L.WBS_LEVEL_ID ORDER BY B.REV_NUMBER DESC) AS rn FROM WBS_LEVEL L LEFT OUTER JOIN BASELINE_RPT B ON L.WBS_LEVEL_ID = B.WBS_LEVEL_ID JOIN BCP BC ON BC.BCP_ID = B.BCP_ID LEFT OUTER JOIN WBS_TREE_MOD W ON L.WBS_LEVEL_ID = W.WBS_LEVEL_ID LEFT OUTER JOIN VW_SITEPEOPLE P1 ON W.WBS_MANAGER_SNUMBER = P1.SNUMBER LEFT OUTER JOIN VW_SITEPEOPLE P2 ON W.PCM_SNUMBER = P2.SNUMBER LEFT OUTER JOIN VW_SITEPEOPLE P3 ON W.PCE_SNUMBER = P3.SNUMBER -- 加上输入参数过滤,减少冗余数据 WHERE L.WBS_LEVEL_ID = in_WBS_LEVEL_ID AND W.FISCAL_YEAR = in_FISCAL_YEAR AND W.FISCAL_MONTH = in_FISCAL_MONTH ) t WHERE t.rn = 1 -- 只保留每个WBS的最新版本记录 ORDER BY t.WBS_LEVEL_NAME; END GET_BASELINE_RPT;
思路2:在最终查询中使用DISTINCT(仅全字段重复时有效)
如果你的重复是因为关联后所有字段完全一致(比如同一个WBS、同一个版本、同一个BCP等),可以把DISTINCT加到子查询里:
SELECT JSON_ARRAYAGG ( JSON_OBJECT (...) RETURNING CLOB ) INTO RET FROM ( SELECT DISTINCT -- 这里加DISTINCT去重全字段重复的记录 L.WBS_LEVEL_NAME, W.DESCRIPTION, B.REV_NUMBER, W.STATUS, BC.FISCAL_YEAR, BC.BCP_FISCAL_ID, BC.APPROVAL_DATE, P1.NICK_NAME, P2.NICK_NAME, P3.NICK_NAME, W.FISCAL_YEAR, W.FISCAL_MONTH, L.WBS_LEVEL_ID FROM WBS_LEVEL L -- 原关联语句+参数过滤 ) t ORDER BY t.WBS_LEVEL_NAME, t.REV_NUMBER DESC;
不过这种情况比较少见,因为如果存在不同的REV_NUMBER,DISTINCT会保留不同版本的记录,WBS仍会重复。
思路3:先排查重复的具体来源
如果不确定是哪个表导致的重复,可以先单独运行不带JSON的查询,定位重复的根源:
SELECT L.WBS_LEVEL_NAME, B.REV_NUMBER, BC.BCP_ID, COUNT(*) AS duplicate_count FROM WBS_LEVEL L LEFT OUTER JOIN BASELINE_RPT B ON L.WBS_LEVEL_ID = B.WBS_LEVEL_ID JOIN BCP BC ON BC.BCP_ID = B.BCP_ID LEFT OUTER JOIN WBS_TREE_MOD W ON L.WBS_LEVEL_ID = W.WBS_LEVEL_ID WHERE L.WBS_LEVEL_ID = in_WBS_LEVEL_ID AND W.FISCAL_YEAR = in_FISCAL_YEAR AND W.FISCAL_MONTH = in_FISCAL_MONTH GROUP BY L.WBS_LEVEL_NAME, B.REV_NUMBER, BC.BCP_ID HAVING COUNT(*) > 1;
通过这个查询可以看到是哪些字段组合(比如WBS+版本+BCP)导致了重复,再针对性调整关联条件或过滤逻辑。
内容的提问来源于stack exchange,提问作者dovakeen117

