You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server嵌套游标异常:仅更新一行,其余行误判未找到

问题描述

在SQL Server中使用嵌套游标编写存储过程时遇到异常:数百万条数据中仅能成功更新一行,其余行被错误判定为“未找到”,但实际数据确实存在。内部游标依赖外部游标的结果,附上存储过程代码,恳请排查错误原因。

存储过程代码
USE [VIVA_LOAD]
GO
/****** Object:  StoredProcedure [dbo].[PAYSHOP_UPDATE_SAFT_NELSON_TESTE]    Script Date: 21/05/2025 19:47:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PAYSHOP_UPDATE_SAFT_NELSON_TESTE]
AS 
BEGIN

DECLARE @v_inv_date  NVARCHAR(256);
DECLARE @data1 datetime; 
DECLARE @entry_date datetime; 
DECLARE @data2 datetime;
DECLARE @V_CARD_LOAD_ID NVARCHAR(256);
DECLARE @V_CARD_serial  NVARCHAR(256);

DECLARE @V_InvoiceNo  NVARCHAR(256);
DECLARE @V_InvoiceDate  datetime;
DECLARE @V_SystemEntryDate datetime;
DECLARE @V_Description  NVARCHAR(256);
DECLARE @V_ProductCode  NVARCHAR(256);


DECLARE Data_Invoice_Usar CURSOR FOR     
SELECT hh.InvoiceNo, hh.InvoiceDate, hh.SystemEntryDate, hh.Description,hh.ProductCode  
FROM PayshopInvoiceLines2 hh 
where Estado_Registos=0;

/*DROP INDEX I_Update_Saft
    ON VIVA_LOAD.dbo.PayshopInvoiceLines2;
CREATE UNIQUE INDEX I_Update_Saft ON VIVA_LOAD.dbo.PayshopInvoiceLines2 (InvoiceNo,InvoiceDate,SystemEntryDate,Description,ProductCode);
*/
        OPEN Data_Invoice_Usar
        FETCH NEXT FROM Data_Invoice_Usar into @V_InvoiceNo, @V_InvoiceDate, @V_SystemEntryDate, @V_Description, @V_ProductCode 

         WHILE @@FETCH_STATUS = 0
            BEGIN

              
                SET @data1=dateadd(month, datediff(month, 0, @V_InvoiceDate), 0)
                               
                SET @data2 = dateadd(MONTH, datediff(MONTH, 1, @V_InvoiceDate)+1, -1);
        
   DECLARE viva_load_cards CURSOR
    FOR
      select cl.CardLoadID, Replace(cr.CardSerialNr,'.','')
      from VIVA_LOAD.dbo.CardLoad cl with(nolock)
        left JOIN VIVA_LOAD.dbo.Product AS p with(nolock) ON p.ProductID = cl.ProductID
        left JOIN VIVA_LOAD.dbo.CardLoadExec AS cle with(nolock) ON cl.CardLoadID = cle.CardLoadID
        inner join viva_load.dbo.CardRead cr with(nolock) on cr.CardReadId = cl.CardReadID
      where 1=1
      and cl.Timestamp >= @data1 and cl.Timestamp <= @data2 -- mes do SAFT
      and CONVERT(varchar(19), cle.Timestamp, 120) <= CONVERT(varchar(19), DATEADD(ss,5,@V_SystemEntryDate), 120) --Invoice.SystemEntryDate com margem de erro de 5 segundos
      and cl.SubEntityID = '12E9C77E-A078-4D3B-A0AB-80CD682E811C' -- Payshop
        and cr.CardSerialNr = @V_Description 
        and p.CatalogProductId = @V_ProductCode;

                OPEN viva_load_cards 
                FETCH NEXT FROM viva_load_cards into @V_CARD_LOAD_ID, @V_CARD_serial
                WHILE @@FETCH_STATUS = 0
                    
                    BEGIN

                              IF @V_CARD_LOAD_ID is not null
                                  IF @V_CARD_serial is not null
                           
                                       UPDATE PayshopInvoiceLines2
                                       SET Card_LoadId_Payshop = @V_CARD_LOAD_ID, Estado_Registos=1
                                       WHERE Description= @V_CARD_serial 
                                       and SystemEntryDate=@V_SystemEntryDate
                                       and Estado_Registos=0
                                       and ProductCode=@V_ProductCode
                                       and InvoiceNo=@V_InvoiceNo
                                       and InvoiceDate=@V_InvoiceDate;
                               ELSE
                                       UPDATE PayshopInvoiceLines2
                                       SET Card_LoadId_Payshop = @V_CARD_LOAD_ID, Estado_Registos=3
                                       wHERE Description= @V_CARD_serial
                                       and SystemEntryDate=@V_SystemEntryDate
                                       and Estado_Registos=0
                                       and ProductCode=@V_ProductCode
                                       and InvoiceNo=@V_InvoiceNo
                                       and InvoiceDate=@V_InvoiceDate;
                            
    FETCH NEXT FROM Data_Invoice_Usar into @V_InvoiceNo, @V_InvoiceDate, @V_SystemEntryDate, @V_Description, @V_ProductCode 

    FETCH NEXT FROM viva_load_cards into @V_CARD_LOAD_ID, @V_CARD_serial
                
                            
                          END;  
                            
                                    CLOSE viva_load_cards;
                            DEALLOCATE viva_load_cards;
     END;

                                UPDATE PayshopInvoiceLines2 
                                SET Estado_Registos=3
                                WHERE Estado_Registos=0;
CLOSE Data_Invoice_Usar;
DEALLOCATE Data_Invoice_Usar;

END;
错误原因排查与修复建议

1. 致命逻辑错误:外部游标被提前推进

内部游标循环中错误加入了FETCH NEXT FROM Data_Invoice_Usar语句,导致外部游标在处理第一行的内部循环时直接跳到下一行,后续大部分外部游标数据未进入处理逻辑,这是仅一行被更新的核心原因。

修复:删除内部循环里的FETCH NEXT FROM Data_Invoice_Usar语句,将其移到外部循环末尾,确保外部游标每处理完一行的所有内部逻辑后再推进:

-- 外部循环末尾添加
FETCH NEXT FROM Data_Invoice_Usar into @V_InvoiceNo, @V_InvoiceDate, @V_SystemEntryDate, @V_Description, @V_ProductCode

2. 数据匹配不一致问题

内部游标查询用Replace(cr.CardSerialNr,'.','')得到@V_CARD_serial,但查询条件是cr.CardSerialNr = @V_Description,UPDATE语句又用Description= @V_CARD_serial。若@V_Description包含.,则@V_CARD_serial是去掉.的版本,与原Description值不匹配,导致UPDATE找不到对应行。

修复:统一匹配规则,二选一即可:

  • 查询时使用去掉.的条件:
    and Replace(cr.CardSerialNr,'.','') = @V_Description
    
  • UPDATE时使用原@V_Description:
    WHERE Description= @V_Description 
    

3. 语法错误

内部游标循环的ELSE分支中,wHERE拼写错误,应为WHERE,会导致该分支的UPDATE语句执行失败。

4. 日期比较效率与准确性问题

通过转换为字符串比较日期CONVERT(varchar(19), cle.Timestamp, 120) <= CONVERT(varchar(19), DATEADD(ss,5,@V_SystemEntryDate), 120),既影响性能,又可能因格式问题导致错误比较。直接用日期类型比较:

and cle.Timestamp <= DATEADD(ss,5,@V_SystemEntryDate)

5. 性能优化建议

数百万条数据使用嵌套游标会导致极低的执行效率,建议改用基于集合的UPDATE操作替代游标,例如通过JOIN直接关联表进行批量更新,大幅提升处理速度。

内容的提问来源于stack exchange,提问作者Nelson Soares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:14:51