8

SQL Server 2000のストアドプロシージャ(現在は更新できません)にカーソルがあり、すべてのテーブルが更新されますが、通常、完了するまでに数分かかります。もっと速くする必要があります。

一方、GDEPO:入口デポ、CDEPO:出口デポ、Adet:数量、使用されるE_CIKAN数量。

説明を記録する:

  1. 20ユニットがデポ01に入り、
  2. 10ユニットは01を残します。
  3. 5ユニットは01を離れます(最初のレコードのE_CIKANは15になります)
  4. さらに10ユニットがデポ01に入ります。
  5. 3ユニットは1番目のレコードから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
4

6 に答える 6

12

この質問に対する私の他の回答での会話に基づいて、私はあなたのルーチンをスピードアップする方法を見つけたと思います.

2 つのネストされたカーソルがあります。

  • 1 つ目は、exitdepot が指定されている各行を選択することです。製品、デポ、金額を受け取り、次のようになります。
  • 内側のカーソル ループは、entrydepot が指定されているその製品/デポの行を通過します。すべての製品が割り当てられるまで、それぞれの E_CIKAN に追加されます。

したがって、内側のカーソル ループは、すべての exitdepot 行に対して少なくとも 1 回実行されます。ただし、システムは、どのアイテムがどのトランザクションで送信されたかを実際には気にしません。最終的な 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 を使用する場合、カーソルは問題に対する最もパフォーマンスの低いソリューションである必要があります。

実際に達成しようとしているものの複雑さに応じて、2 つのオプションがあります。

  1. セット演算を使用するようにコード セット全体を書き直してみてください。これは最速の実行方法ですが、集合操作を使用して実行できない場合があります。

  2. カーソルをテーブル変数 (ID 列を含む)、カウンター、および while ループの組み合わせに置き換えます。その後、テーブル変数の各行をループできます。カーソルよりも優れたパフォーマンスを発揮します...そうは思えないかもしれませんが。

于 2009-05-13T18:08:11.960 に答える
1

あなたが解決しようとしている問題は非常に複雑であることがわかります。

  • GDEPO が指定された行がある場合、それはデポに入る在庫を表し、その行の E_CIKAN を使用して、後で在庫がどれだけ使用されるかを追跡します。E_CIKAN は 0 から始まり、ADET に達するまで在庫がなくなると追加されます。

  • CDEPO が指定された後続の行がある場合、それは在庫切れを表し、GDEPO 行の E_CIKAN に戻り、在庫切れの量を追加して E_CIKAN を調整します。

  • 在庫が入っている行が 2 行ある場合 (GDEPO 指定)、1 行の E_CIKAN が最大 (ADET) に達したときにオーバーフローが発生し、残りを次の行に追加したい場合があります。

これは非常に難しい計算です。異なる行を比較し、1 行または 2 行で戻って値を変更し、各株式取引を追跡する必要があるからです。

他の人が示唆しているように、カーソルなしでそれを行う方法があるかもしれません。しかし、テーブルを再配置してデータを別の方法で保存できれば、問題を簡単に解決できると思います。

たとえば、在庫取引を記録する同じテーブルで在庫を追跡する代わりに、「Product_id、Depo_id、金額」列を含む別のテーブルを作成して、各デポの各製品の合計金額を一度に追跡できますか? ?

このようなデータベース設計の変更により、作業が容易になる可能性があります。

または ... E_CIKAN を使用して何が使用されているかを追跡する代わりに、E_CIKAN を使用して残っているものを追跡します。各行に E_CIKAN 値を保持します。したがって、在庫がデポに出入りするたびに、その時点でE_CIKAN を再計算し、そのトランザクション行に格納します (元の「在庫」行に戻ってそこで更新しようとするのではなく)。次に、現在の在庫を確認するには、その製品/デポの最新のトランザクションを確認します。

要約すると、私が言いたいのは、奇妙な方法でデータを保存しているため、計算が遅くて面倒だということです。長期的には、データベースの設計を変更してプログラミングを容易にする価値があるかもしれません。

于 2009-05-13T19:47:43.950 に答える
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 に答える