如何使T-SQL游标更快?

Ert*_*ara 8 sql sql-server cursor

嘿,我在SQL Server 2000下的存储过程中有一个游标(现在无法更新),它更新了所有表,但通常需要几分钟才能完成.我需要让它更快.这是由任意产品ID过滤的示例表; 示例表http://img231.imageshack.us/img231/9464/75187992.jpg 而GDEPO:Entry depot,CDEPO:Exit depot,Adet:quantity,E_CIKAN使用的数量.

记录解释:
1:20单位进入仓库01,2:10单位叶01. 3:5单位叶01(E_CIKAN为第1记录现在为15)4:10单位进入仓库01. 5:3单位离开01从第1记录.请注意,现在第一条记录的E_CIKAN设置为18. 6:这就是问题所在:3个单元需要离开库01.从第1条记录开始需要2个单元,从第5条记录需要1个单元.我的SP可以处理这个如图所示的罚款,除非它真的很慢.

这是存储过程翻译成英文;

CREATE PROC [dbo].[UpdateProductDetails]
as
UPDATE PRODUCTDETAILS SET E_CIKAN=0;
DECLARE @ID int
DECLARE @SK varchar(50),@DP varchar(50)  --SK = STOKKODU = PRODUCTID, DP = DEPOT
DECLARE @DEMAND float     --Demand=Quantity, We'll decrease it record by record
DECLARE @SUBID int
DECLARE @SUBQTY float,@SUBCK float,@REMAINS float
DECLARE SH CURSOR FAST_FORWARD FOR
SELECT [ID],PRODUCTID,QTY,EXITDEPOT FROM PRODUCTDETAILS  WHERE (EXITDEPOT IS NOT NULL) ORDER BY [DATE] ASC
OPEN SH
FETCH NEXT FROM SH INTO @ID, @SK,@DEMAND,@DP

WHILE (@@FETCH_STATUS = 0)
BEGIN
   DECLARE SA CURSOR FAST_FORWARD FOR
   SELECT [ID],QTY,E_CIKAN FROM PRODUCTDETAILS  WHERE (QTY>E_CIKAN) AND (PRODUCTID=@SK) AND (ENTRYDEPOT=@DP) ORDER BY [DATE] ASC
   OPEN SA
   FETCH NEXT FROM SA INTO @SUBID, @SUBQTY,@SUBCK
   WHILE (@@FETCH_STATUS = 0) AND (@DEMAND>0)
   BEGIN
      SET @REMAINS=@SUBQTY-@SUBCK
      IF @DEMAND>@REMAINS  --current record isnt sufficient, use it and move on
      BEGIN
         UPDATE PRODUCTDETAILS SET E_CIKAN=QTY WHERE ID=@SUBID;
         SET @DEMAND=@DEMAND-@REMAINS
      END
      ELSE
      BEGIN
         UPDATE PRODUCTDETAILS SET E_CIKAN=E_CIKAN+@DEMAND WHERE ID=@SUBID;
         SET @DEMAND=0
      END
      FETCH NEXT FROM SA INTO @SUBID, @SUBAD,@SUBCK
   END
   CLOSE SA
   DEALLOCATE SA
   FETCH NEXT FROM SH INTO @ID, @SK,@DEMAND,@DP
END
CLOSE SH
DEALLOCATE SH
Run Code Online (Sandbox Code Playgroud)

cod*_*ike 11

根据我在这个问题的另一个答案中的对话,我想我已经找到了加快日常工作的方法.

你有两个嵌套游标:

  • 第一个是选择具有指定的exitdepot的每一行.它需要产品,depo和数量,然后:
  • 内部游标循环遍历指定了entrydepot的产品/库的行.它为每一个添加了E_CIKAN,直到它分配了所有产品.

因此,对于您拥有的每个exitdepot行,内部游标循环至少运行一次.但是,您的系统并不真正关心哪些项目与哪个事务发生了关系 - 您只是在尝试计算最终的E_CIKAN值.

所以......

您的外部循环只需要获得每个产品/仓库组合的运出物品总量.因此,您可以将外部游标定义更改为:

DECLARE SH CURSOR FAST_FORWARD FOR
    SELECT PRODUCTID,EXITDEPOT, Sum(Qty) as TOTALQTY
    FROM PRODUCTDETAILS  
    WHERE (EXITDEPOT IS NOT NULL) 
    GROUP BY PRODUCTID, EXITDEPOT
OPEN SH
FETCH NEXT FROM SH INTO @SK,@DP,@DEMAND
Run Code Online (Sandbox Code Playgroud)

(然后还要在代码末尾从SH更改匹配的FETCH以匹配,显然)

这意味着你的外部游标将有更少的行来循环,你的内部游标将具有相同数量的行来循环.

所以这应该更快.

  • 对于其他读者:这是"运行总计"查询问题的变体,因此可能实际上是游标提供最快(可靠)解决方案的极少数情况之一.有关SqlServer运行总计的一般详细信息,请参阅此另一个问题:http://stackoverflow.com/questions/860966/calculate-a-running-total-in-sqlserver (4认同)