执行MSSQL存储过程时PB12.5采购模块无法打开的问题
问题描述
我们有一个MSSQL存储过程,用于将合同数据从SAP对接的中间数据库同步到我方数据库,包含循环逻辑和多表操作。同时部署了PB12.5桌面应用,涵盖采购、船员管理、员工等模块。
现象:执行该存储过程耗时约2分钟(依数据量而定),执行期间无法打开采购窗口,必须等存储过程完成才能打开;但员工窗口可正常打开。
已排查内容
- 采购模块使用table1和table2,当前存储过程未操作这两张表(后续扩展会用到)
- 存储过程执行期间,可正常执行
select * from table1查询采购模块表 - 存储过程未锁定任何表
- 多台PC测试均存在相同问题
- 后续计划扩展存储过程,增加更多表和多游标循环
尝试添加事务控制语句(Begin tran和commit Tran),但未解决问题。
存储过程部分代码
alter PROC [spectwosuite].[CST_GENERATE_IPurchase_contract] as begin SET NOCOUNT ON; /**/ --truncate table spectwosuite.CUSTOM_SAP_CONTRACT_ITEM; --truncate table spectwosuite.CUSTOM_SAP_CONTRACT_header; --truncate table spectwosuite.CUSTOM_SAP_PROC_LOG; --Variable declaration DECLARE @MATERIALNO VARCHAR(20); DECLARE @PURCHASEDOCNO VARCHAR(20); DECLARE @PORTID NUMERIC(15); DECLARE @PRICE DECIMAL(11,2); DECLARE @PKID NUMERIC(16); DECLARE @STOCKTYPEID NUMERIC(15); DECLARE @STOCKTYPECODE VARCHAR(50); DECLARE @STNAME VARCHAR(50); DECLARE @UNITID NUMERIC(15); DECLARE @BUSFLOWID NUMERIC(15); DECLARE @BUSSTATUSID NUMERIC(15); DECLARE @ITEMNO VARCHAR(5); DECLARE @SUBITEMNO VARCHAR(10); DECLARE @STOCKDISID NUMERIC(15); DECLARE @PURCONTRACTID NUMERIC(15); DECLARE @REVISIONNO NUMERIC(15); DECLARE @DESCR NVARCHAR(100); DECLARE @ADDRESSID NUMERIC(15); DECLARE @DELTERMSID NUMERIC(15); DECLARE @PAYTERMSID NUMERIC(15); DECLARE @VALIDSTART DATETIME; DECLARE @VALIDEND DATETIME; DECLARE @CURRENCY NCHAR(3); DECLARE @PRODUCTGROUPID NUMERIC(15); DECLARE @PURCONTRACTPRODUCTGROUPID NUMERIC(15); DECLARE @MATERIALGRP NVARCHAR(20); DECLARE @NETPRICE NUMERIC(16,6); DECLARE @BASEPRICE NUMERIC(16,6); DECLARE @PRODUCTGROUPLINEID NUMERIC(15); DECLARE @SECTION VARCHAR(20); DECLARE @PUREXIST NUMERIC(1); DECLARE @HEADERPKID NUMERIC(16); DECLARE @ITEMPRICE NUMERIC(16,6); DECLARE @BASEITEMPRICE NUMERIC(16,2); DECLARE @PURCONTRACTVARIABLEID NUMERIC(15); DECLARE @FACTORVALUE NUMERIC(9,4); DECLARE @ITEMPERCENTAGE NUMERIC(15,2); DECLARE @ITEMPRICEDIFF1 NUMERIC(15,2); DECLARE @ITEMPRICEDIFF2 NUMERIC(15,2); DECLARE @ITEMPKID NUMERIC(16); /*CONT1: Transfering all the portid and purchase document number to different table */ --PRINT 'CONT1 START' --PRINT GETDATE(); BEGIN TRY --BEGIN TRANSACTION --BEGIN SET @SECTION='CONT1'; BEGIN TRANSACTION CONT1 INSERT INTO SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_PORT (PORTID ,PURCHASE_DOC_NO ) SELECT DISTINCT PORTID,PURCHASE_DOC_NO FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE IMPORT_STATUS IS NULL and DELETION_IND NOT IN('L','1') and portid>0 and MATERIAL_NO<>'' and FACTOR>0 and not exists (select PORTID from SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_PORT where PORTID=SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_PORT.PORTID and PURCHASE_DOC_NO =SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_PORT.PURCHASE_DOC_NO) ORDER BY PURCHASE_DOC_NO,PORTID ASC; COMMIT TRANSACTION CONT1 --END ----INSERT INTO spectwosuite.CUSTOM_SAP_PROC_LOG ---- (STATUS ---- ,DESCRIPTION ---- ,PROCESSEDDATE ---- ,RECORDCOUNT) ---- VALUES ---- (1, ---- 'CONT1' ---- ,GETDATE() ---- ,@@ROWCOUNT ---- ); --PRINT 'CONT1 END' --PRINT GETDATE(); /* CONT1 END */ /*CONT2 Inserting new item which one dont have price in all ports*/ /*Variable declaration*/ --PRINT 'CONT2 START' --PRINT GETDATE(); -- BEGIN SET @SECTION='CONT2'; --SELECT DISTINCT CAST(MATERIAL_NO AS VARCHAR(20))AS MATERIAL_NO,PURCHASE_DOC_NO FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE PURCHASE_DOC_NO='4600000820' and IMPORT_STATUS IS NULL and MATERIAL_NO<>'' and DELETION_IND NOT IN('L','1') and len(material_no)>0 and factor>0 and portid>0 ORDER BY MATERIAL_NO,PURCHASE_DOC_NO ASC; DECLARE CONT2CR CURSOR FOR SELECT DISTINCT CAST(MATERIAL_NO AS VARCHAR(20))AS MATERIAL_NO,PURCHASE_DOC_NO FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE IMPORT_STATUS IS NULL and MATERIAL_NO<>'' and DELETION_IND NOT IN('L','1') and len(material_no)>0 and factor>0 and portid>0 ORDER BY MATERIAL_NO,PURCHASE_DOC_NO ASC; OPEN CONT2CR FETCH NEXT FROM CONT2CR INTO @MATERIALNO,@PURCHASEDOCNO WHILE @@FETCH_STATUS=0 BEGIN BEGIN TRAN CONT2 select @PKID=MIN(pk_id),@PRICE=MIN(NET_PRICE) from SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE MATERIAL_NO=@MATERIALNO AND PURCHASE_DOC_NO=@PURCHASEDOCNO and IMPORT_STATUS IS NULL and DELETION_IND NOT IN('L','1') and factor>0 and portid>0; /*Retreiving port details table*/ DECLARE CONT2PORTCR CURSOR FOR SELECT DISTINCT portid FROM SAP_INTERFACE.spectwosuite.custom_sap_contract_port WITH(NOLOCK) WHERE (SAP_INTERFACE.spectwosuite.custom_sap_contract_port.purchase_doc_no = @PURCHASEDOCNO) and ( custom_sap_contract_port.portid > 0 ) ; OPEN CONT2PORTCR FETCH NEXT FROM CONT2PORTCR INTO @PORTID WHILE @PORTID>0 BEGIN IF @PORTID>0 BEGIN BEGIN TRAN /*Insert Script*/ SET @SECTION='ITEM_ADDITION'; IF (select COUNT(*) from SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) where MATERIAL_NO=CAST(@MATERIALNO AS varchar(20)) AND PORTID=@PORTID and PURCHASE_DOC_NO=@PURCHASEDOCNO and IMPORT_STATUS IS NULL and DELETION_IND NOT IN('L','1') and factor>0 and portid>0)<=0 BEGIN INSERT INTO SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM (DELETION_IND,PURCHASE_DOC_NO,ITEM_NO,MATERIAL_NO,SHORT_TEXT,MATERIAL_GRP,QTY,UOM,NET_PRICE,PORTID,TEMPPORTID,FACTOR) select DELETION_IND,PURCHASE_DOC_NO,ITEM_NO,MATERIAL_NO,SHORT_TEXT,MATERIAL_GRP,QTY,UOM,NET_PRICE,@PORTID,1252,FACTOR from SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE pk_id=@PKID and DELETION_IND<>'1' and IMPORT_STATUS IS NULL; END SET @PORTID=0; COMMIT TRAN END FETCH NEXT FROM CONT2PORTCR INTO @PORTID END CLOSE CONT2PORTCR DEALLOCATE CONT2PORTCR COMMIT TRAN CONT2 FETCH NEXT FROM CONT2CR INTO @MATERIALNO,@PURCHASEDOCNO END CLOSE CONT2CR DEALLOCATE CONT2CR --END --INSERT INTO spectwosuite.CUSTOM_SAP_PROC_LOG -- (STATUS -- ,DESCRIPTION -- ,PROCESSEDDATE -- ,RECORDCOUNT) -- VALUES -- (1, -- 'CONT2' -- ,GETDATE() -- ,@@ROWCOUNT -- ); --PRINT 'CONT2 END' --PRINT GETDATE(); /*CONT2 END*/ /*CONT3*/ --PRINT 'CONT3 START' --PRINT GETDATE(); SET @SECTION='CONT3'; BEGIN TRAN CONT3 INSERT INTO spectwosuite.CUSTOM_SAP_CONTRACT_HEADER (TABLEPKID ,PURCHASE_DOC_NO ,COMPANY_CODE ,CREATED_ON_DATE ,VENDOR_CODE ,PAYMENT_TERMS ,PURCHASE_GRP ,VALIDITY_START ,VALIDITY_END ,YOUR_REFERENCE ,OUR_REFERNCE ,TARGET_AMOUNT ,CURRENCY ,DESCRIPTION ,SHIPCODE ,STATUS ,CREATEDATE ,VALIDATESTART ,VALIDATEEND ,INTERNAL_STATUS ,INTERNAL_MESSAGE ) SELECT PK_ID ,PURCHASE_DOC_NO ,COMPANY_CODE ,CREATED_ON_DATE ,VENDOR_CODE ,PAYMENT_TERMS ,PURCHASE_GRP ,VALIDITY_START ,VALIDITY_END ,YOUR_REFERENCE ,OUR_REFERNCE ,TARGET_AMOUNT ,CURRENCY ,DESCRIPTION ,SHIPCODE ,STATUS ,CREATEDATE ,VALIDATESTART ,VALIDATEEND ,INTERNAL_STATUS ,INTERNAL_MESSAGE FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_HEADER WITH(NOLOCK) where import_status is null and purchase_doc_no<>'' and validity_start<>'' and validity_end<>'' and currency<>''and status>0 and not exists(select pk_id from spectwosuite.CUSTOM_SAP_CONTRACT_HEADER WHERE TABLEPKID= SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_HEADER.PK_ID) COMMIT TRAN CONT3 --INSERT INTO spectwosuite.CUSTOM_SAP_PROC_LOG -- (STATUS -- ,DESCRIPTION -- ,PROCESSEDDATE -- ,RECORDCOUNT) -- VALUES -- (1, -- 'CONT3' -- ,GETDATE() -- ,@@ROWCOUNT -- ); --PRINT 'CONT3 END' --PRINT GETDATE(); /*CONT3 END*/ /*CONT4*/ --PRINT 'CONT4 START' --PRINT GETDATE(); SET @SECTION='CONT4'; BEGIN TRAN CONT4 INSERT INTO spectwosuite.CUSTOM_SAP_CONTRACT_ITEM (TABLEPKID,PURCHASE_DOC_NO ,ITEM_NO ,DELETION_IND ,MATERIAL_NO ,SHORT_TEXT ,MATERIAL_GRP ,QTY ,UOM ,NET_PRICE ,PORTID ,STOCKTYPEID ,INTERNAL_MESSAGE ,INTERNAL_STATUS ,CONTRACTTYPE ,AGREEMENTSUBITEMNO ,FACTOR ) select PK_ID,PURCHASE_DOC_NO ,ITEM_NO ,DELETION_IND ,MATERIAL_NO ,SHORT_TEXT ,MATERIAL_GRP ,QTY ,UOM ,NET_PRICE ,PORTID ,STOCKTYPEID ,INTERNAL_MESSAGE ,INTERNAL_STATUS ,CONTRACTTYPE ,AGREEMENTSUBITEMNO ,FACTOR from SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) where IMPORT_STATUS is null and MATERIAL_NO<>'' and DELETION_IND<>'L' AND PORTID>0 and FACTOR>0 and LEN(SHORT_TEXT)>0 AND LEN(PURCHASE_DOC_NO)>0 AND not exists (select PK_ID from spectwosuite.CUSTOM_SAP_CONTRACT_ITEM where TABLEPKID= SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM.PK_ID ) ORDER BY PK_ID ASC; COMMIT TRAN CONT4 -- /*Inserting History*/ -- /*Transfering process status to interface table.*/ TRUNCATE TABLE spectwosuite.CUSTOM_SAP_CONTRACT_HEADER; TRUNCATE TABLE spectwosuite.CUSTOM_SAP_CONTRACT_ITEM; DELETE FROM spectwosuite.CUSTOM_SAP_PROC_LOG WHERE STATUS=99; COMMIT -- /*PURCHASE CONTRACT CREATION END*/ -- SET NOCOUNT OFF; -- --COMMIT TRANSACTION END TRY BEGIN CATCH DECLARE @ERRORNUMBER VARCHAR(20); DECLARE @ERRMSG VARCHAR(MAX); SET @ERRORNUMBER=CAST(ERROR_LINE() AS VARCHAR(20)); SET @ERRMSG=ERROR_MESSAGE(); BEGIN TRAN ER INSERT INTO spectwosuite.CUSTOM_SAP_PROC_LOG (STATUS ,DESCRIPTION ,PROCESSEDDATE ,RECORDCOUNT ,ERRORMSG) VALUES ( 88, @SECTION ,getdate() ,1 ,@ERRORNUMBER +','+ @ERRMSG ); COMMIT TRAN ER END CATCH END
问题分析与解决方案
核心原因推测
虽然存储过程未直接操作采购模块的table1和table2,但PB12.5的采购窗口大概率使用了同一数据库连接,存储过程执行时长时间占用连接资源,导致采购窗口的请求被阻塞。员工窗口正常是因为其使用的连接或资源未被存储过程占用。
另外,存储过程中嵌套游标(CONT2CR套CONT2PORTCR)的设计会持续占用数据库连接,加剧资源竞争;同时未合理优化事务和锁策略,可能引发隐性的资源冲突。
具体解决步骤
分离数据库连接
- 配置PB12.5应用,为采购模块单独分配数据库连接池,避免与存储过程共享同一连接。这样存储过程执行时,采购窗口可通过独立连接访问数据库。
改为异步执行存储过程
- 使用SQL Server代理作业,通过
sp_start_job调用存储过程,让存储过程在后台异步执行,不占用应用前台连接。 - 或在PB应用中通过后台线程调用存储过程,避免阻塞UI操作。
- 使用SQL Server代理作业,通过
重构存储过程,替换游标为集合操作
嵌套游标是性能和资源占用的主要瓶颈,将CONT2部分的游标逻辑改为纯SQL集合操作,大幅减少执行时间和资源占用:-- 替换CONT2的游标逻辑 INSERT INTO SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM (DELETION_IND,PURCHASE_DOC_NO,ITEM_NO,MATERIAL_NO,SHORT_TEXT,MATERIAL_GRP,QTY,UOM,NET_PRICE,PORTID,TEMPPORTID,FACTOR) SELECT c.DELETION_IND,c.PURCHASE_DOC_NO,c.ITEM_NO,c.MATERIAL_NO,c.SHORT_TEXT,c.MATERIAL_GRP,c.QTY,c.UOM,c.NET_PRICE,p.PORTID,1252,c.FACTOR FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM c CROSS JOIN SAP_INTERFACE.spectwosuite.custom_sap_contract_port p JOIN ( SELECT MATERIAL_NO,PURCHASE_DOC_NO,MIN(pk_id) AS PK_ID FROM SAP_INTERFACE.spectwosuite.CUSTOM_SAP_CONTRACT_ITEM WITH(NOLOCK) WHERE IMPORT_STATUS IS NULL AND DELETION_IND NOT IN('L','1') AND len(material_no)>0 AND factor>0 AND portid>0 GROUP BY MATERIAL_NO,PURCHASE_DOC_NO ) min_c ON c.pk_id = min_c.PK_ID WHERE NOT
相关产品推荐
相关产品推荐

