8

我在 SQL Server 2000 下的存储过程中有一个游标(现在无法更新),它更新了所有表,但通常需要几分钟才能完成。我需要让它更快。

而 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 个单位从第 1 条记录中留下 01。注意现在第一条记录的 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
4

6 回答 6

12

根据我们在我对这个问题的其他回答中的对话,我想我已经找到了一种方法来加快你的日常生活。

您有两个嵌套游标:

  • 第一个是选择指定了 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

(然后在代码末尾将匹配的 FETCH 从 SH 更改为匹配,显然)

这意味着您的外部光标将有更少的行来循环,而您的内部光标将具有大致相同数量的行来循环。

所以这应该更快。

于 2009-05-13T20:35:43.500 回答
2

在使用 T-SQL 时,游标必须是解决任何问题的最差解决方案。

根据您真正想要完成的任务的复杂性,您有两种选择:

  1. 尝试重写整个代码集以使用集合操作。这将是最快的执行方法......但有时您无法使用集合操作来做到这一点。

  2. 将光标替换为表变量(带有标识列)、计数器和 while 循环的组合。然后,您可以遍历表变量的每一行。比光标执行得更好......即使它看起来不像。

于 2009-05-13T18:08:11.960 回答
1

删除游标并将其重写为 UPDATE FROM 加入游标的查询,如果需要,您可以使 IF 成为案例。今天太忙了,没时间给你写更新……

于 2009-05-13T18:03:54.900 回答
1

移除光标并进行批量更新。我还没有找到不能批量完成的更新。

于 2009-05-13T18:04:50.127 回答
1

首先,如果您必须使用游标,并且要更新内容,则使用 FOR UPDATE 子句声明游标。(请参见下面的示例。请注意,该示例根本不是基于您的代码。)

话虽如此,有无数种方法可以使用游标以外的东西,通常是利用临时表。我会调查那条路线来代替游标。

DECLARE LoopingCursor CURSOR LOCAL DYNAMIC
FOR
    select sortorder from customfielddefinition
    where context=@targetContext
FOR UPDATE OF sortorder
于 2009-05-13T18:17:59.790 回答
1

我可以看到您要解决的问题非常复杂:

  • 当存在指定 GDEPO 的行时,它表示库存进入仓库,您希望使用该的 E_CIKAN来跟踪以后使用了多少库存。E_CIKAN 将从 0 开始,然后在库存耗尽时添加,直到达到 ADET。

  • 因此,当有指定 CDEPO 的后续行时,它表示缺货,您想返回 GDEPO 行的 E_CIKAN 并通过添加缺货量来调整 E_CIKAN。

  • 当有两个单独的行进入库存时(指定 GDEPO),有时当一行的 E_CIKAN 达到最大值(ADET)时会出现溢出,然后您想将剩余部分添加到下一行。

这是一个相当棘手的计算,因为您必须比较不同的行并返回并更改可能一或两行中的值以跟踪每个股票交易。

正如其他人所建议的那样,可能有一种方法可以在没有光标的情况下做到这一点。但是我认为,如果您可以重新排列表格并以不同的方式存储数据,则可能会使问题变得更容易。

例如,不是在记录库存交易的同一个表中跟踪库存,您是否可以有一个单独的表,其中包含“Product_id,Depo_id,amount”列,一次跟踪每个仓库中每种产品的总量?

诸如此类的数据库设计更改可以使事情变得更容易。

或者...而不是使用 E_CIKAN 来跟踪使用的内容而是使用它来跟踪剩余的内容。并在每一行中保留一个 E_CIKAN 值。因此,每当库存进出仓库时,请在该时间点重新计算 E_CIKAN并将其存储在该事务行中(而不是尝试返回到原始的“入库”行并在那里更新它)。然后要找出当前库存,您只需查看该产品/仓库的最新交易。

总而言之,我的意思是,您的计算缓慢而繁琐,因为您以一种奇怪的方式存储数据。从长远来看,可能值得更改您的数据库设计以使编程更容易。

于 2009-05-13T19:47:43.950 回答