一、修改背景与目标
原存储过程逻辑为删除并全量重建采购入库单扩展字段表(`rdrecords01_extradefine`)中的请购部门信息(`cbdefine7`),存在以下问题:
1. 数据覆盖风险:全量删除可能导致历史数据丢失(若上游数据异常)。
2. 性能损耗:对大量数据频繁删除+插入,易引发锁表或事务日志膨胀。
优化目标:
仅对新增/更新的入库单记录(即`cbdefine7`为空或变更的记录)执行更新操作,保留历史有效数据。
二、核心逻辑调整
1. 移除全量删除操作
删除原存储过程开头的 `DELETE FROM UFDATA_004_2017.dbo.rdrecords01_extradefine` 语句,避免全表清空。
2. 调整CTE查询范围
CTE(公用表表达式)仅筛选需要更新的记录:
- 采购入库单列表(`zpurRkdList`)中未维护请购部门的记录(即`rdrecords01_extradefine.cbdefine7 IS NULL`)。
- 或需增量更新的记录(可通过时间戳/版本号判断,此处暂以`NULL`为例)。
3. 从“删除+插入”改为“条件更新”
通过 `MERGE` 或 `UPDATE` 语句,将CTE计算出的请购部门信息增量更新到扩展字段表,而非全量重建。
三、修改后的存储过程代码
```sql
CREATE PROCEDURE AA_extradefine_rdrecords01_cbdefine7
AS
BEGIN
SET NOCOUNT ON; -- 关闭计数,提升性能
-- 定义CTE:计算需要更新的请购部门信息(仅包含cbdefine7为空的记录)
WITH CTE AS (
SELECT
zpurrkdlist.autoid,
ISNULL(MAX(tt.ccode), MAX(dd.ccode)) AS 请购单, -- 优先取采购订单来源的请购单,其次委外订单
ISNULL(MAX(tt.采购请购部门), MAX(dd.委外请购部门)) AS 部门 -- 合并采购/委外来源的请购部门(取最新单据)
FROM
zpurRkdList WITH (NOLOCK) -- 采购入库单列表(nolock避免阻塞)
-- 关联扩展字段表,筛选cbdefine7为空的记录(需更新的数据)
LEFT JOIN rdrecords01_extradefine ex WITH (NOLOCK)
ON ex.autoid = zpurrkdlist.autoid
AND ex.cbdefine7 IS NULL -- 仅处理未维护请购部门的记录
-- 关联采购订单信息(含请购部门)
LEFT JOIN (
SELECT
cpoid AS 订单编号,
cinvcode AS 存货编码,
cdepname2 AS 采购请购部门,
ccode -- 请购单编号(用于取最新)
FROM zpurpoheader ph WITH (NOLOCK)
INNER JOIN zpurpotail pt WITH (NOLOCK) ON ph.poid = pt.poid
LEFT JOIN pu_AppHead ah WITH (NOLOCK) ON ph.cupsocode = ah.ccode -- 关联请购单头表获取部门
) tt ON zpurrkdlist.cpoid = tt.订单编号 AND zpurrkdlist.cinvcode = tt.存货编码
-- 关联委外订单信息(含请购部门)
LEFT JOIN (
SELECT
om_mohead.ccode AS 订单编号, -- 委外订单编号
om_mobody.cinvcode AS 存货编码,
cdepname2 AS 委外请购部门,
cc.ccode -- 请购单编号(用于取最新)
FROM om_mohead WITH (NOLOCK)
INNER JOIN om_mobody WITH (NOLOCK) ON om_mohead.moid = om_mobody.moid
LEFT JOIN (
SELECT ccode, cdepname AS cdepname2 FROM pu_AppHead -- 委外请购部门来源
) cc ON om_mobody.cupsocode = cc.ccode
) dd ON zpurrkdlist.cpoid = dd.订单编号 AND zpurrkdlist.cinvcode = dd.存货编码
WHERE
ex.autoid IS NOT NULL -- 仅处理已存在于扩展表但cbdefine7为空的记录
-- 可添加时间条件实现增量更新,例如:zpurrkdlist.dDate >= DATEADD(DAY, -1, GETDATE())
GROUP BY
zpurrkdlist.autoid -- 按入库单明细行分组
)
-- 增量更新扩展字段表:将CTE计算的部门信息更新到rdrecords01_extradefine.cbdefine7
UPDATE ex
SET ex.cbdefine7 = cte.部门
FROM rdrecords01_extradefine ex
INNER JOIN CTE ON ex.autoid = CTE.autoid
WHERE CTE.部门 IS NOT NULL; -- 仅更新有有效部门信息的记录
-- (可选)若扩展表中不存在记录,补充插入(避免漏导)
INSERT INTO rdrecords01_extradefine (autoid, cbdefine7)
SELECT
cte.autoid,
cte.部门
FROM CTE
LEFT JOIN rdrecords01_extradefine ex ON ex.autoid = cte.autoid
WHERE
ex.autoid IS NULL -- 扩展表中不存在该autoid的记录
AND cte.部门 IS NOT NULL; -- 确保部门信息有效
SET NOCOUNT OFF;
END
```
四、关键优化点说明
1. 增量更新逻辑:
- 通过 `LEFT JOIN rdrecords01_extradefine ex ON ... AND ex.cbdefine7 IS NULL` 筛选需更新的记录,避免全表扫描。
- 若需支持历史数据修复,可移除 `ex.cbdefine7 IS NULL` 条件,或增加时间范围(如近1个月未更新的记录)。
2. 数据优先级处理:
- 请购部门优先取采购订单来源(`tt.采购请购部门`),其次委外订单(`dd.委外请购部门`),通过 `ISNULL(MAX(tt...), MAX(dd...))` 实现。
- 若同一入库单行对应多个请购单,通过 `MAX(tt.ccode)/MAX(dd.ccode)` 取最新请购单的部门(假设`ccode`为按时间递增的单号)。
3. 性能优化:
- 使用 `WITH (NOLOCK)` 减少锁竞争(需业务允许脏读)。
- 通过 `GROUP BY autoid` 确保每个入库单明细行只更新一次。
- 拆分“更新”与“插入”逻辑,避免 `MERGE` 语句的潜在死锁风险。
五、使用建议
1. 执行频率:建议按小时/日调度执行,避免实时触发(如每小时更新近24小时新增的入库单)。
2. 数据校验:更新后可通过以下SQL验证结果:
```sql
SELECT TOP 100 autoid, cbdefine7 AS 请购部门
FROM rdrecords01_extradefine
WHERE cbdefine7 IS NOT NULL
ORDER BY autoid DESC;
```
3. 异常处理:可添加 `TRY...CATCH` 块记录错误日志,例如:
```sql
BEGIN TRY
-- 主逻辑
END TRY
BEGIN CATCH
INSERT INTO ErrorLog (ProcName, ErrorMsg, ErrorTime)
VALUES ('AA_extradefine_rdrecords01_cbdefine7', ERROR_MESSAGE(), GETDATE());
END CATCH
```
六、总结
修改后的存储过程通过增量更新替代全量重建,显著降低了数据操作风险和性能损耗,同时保留了原有的请购部门逻辑(采购订单优先、多来源合并、取最新请购单)。适用于用友U8等ERP系统中采购入库单扩展字段的日常维护场景。
转载请注明来自海坡下载,本文标题:《存储过程的优化(存储过程修改说明及优化方案)》
京公网安备11000000000001号
京ICP备11000001号
还没有评论,来说两句吧...