2017-11-28 37 views
0

ここでは、私が期待していたよりも少し時間がかかりそうなストアドプロシージャがあります。このアルゴリズムを非常に長くしたのは、SQLの新しいテーブルに要素を追加すると、必ずしもトップに置かれないように思えたからです。ルーピングストアドプロシージャが遅すぎる

また、別のテーブルを作成したクエリをクエリする必要がありました。全体として、このアルゴリズムは大きすぎます。誰にも短くするためのアイデアはありますか?

DECLARE @RecipeQuery TABLE(
RecipeID NCHAR(100), 
MaterialID NCHAR(100), 
Quantity DECIMAL(18,4)); 

INSERT INTO @RecipeQuery 
(RecipeID, MaterialID, Quantity) 
SELECT RecipeID, MaterialID, Quantity 
FROM Recipe 

DECLARE @PrevRecipeQuery TABLE(
prevRecipeID NCHAR(100), 
prevMaterialID NCHAR(100), 
prevQuantity DECIMAL(18,4)); 

DECLARE @TempRecipeQuery TABLE(
TempRecipeID NCHAR(100), 
TempMaterialID NCHAR(100), 
TempQuantity DECIMAL(18,4)); 

DECLARE @MaterialFinder TABLE(
RID NCHAR(100), 
MID NCHAR(100), 
Q DECIMAL(18,4)); 

DECLARE @PrevMaterialFinder TABLE(
PRID NCHAR(100), 
PMID NCHAR(100), 
PQ DECIMAL(18,4)); 

DECLARE @CalcMaterial TABLE(
CalcRecipeID NCHAR(100), 
CalcMaterialID NCHAR(100), 
CalcQuantity DECIMAL(18,4)); 

DECLARE @ROWCOUNT1 INT 
SET @ROWCOUNT1 = 0 

DECLARE @ROWCOUNT2 INT 
SET @ROWCOUNT2 = 0 

DECLARE @ROWCOUNT3 INT 
SET @ROWCOUNT3 = 0 

DECLARE @isDone INT 
SET @isDone = 0 

DECLARE @mainRowCount INT 

-------------------------------------------------------------------------------------------------- 

--LOOP UNTIL ALL LEVELS HAVE BEEN FOUND 
WHILE(@isDone != 1) 
BEGIN 

SET @mainRowCount = (SELECT COUNT(*) FROM @RecipeQuery) 

--LOOP THROUGH EACH ROW IN THE TABLE UNTIL ALL ROWS HAVE BEEN LOOKED AT 
WHILE(@ROWCOUNT1 < @mainRowCount) 
BEGIN 

--IF THE ROW'S MATERIAL STARTS WITH A "TempRecipeID" THAT MEANS IT IS DONE 
IF(PATINDEX('R%', (SELECT TOP 1 MaterialID FROM @RecipeQuery)) = 1) 
BEGIN 
--INSERT THE ROW RIGHT INTO TABLE new 
    INSERT INTO @CalcMaterial (CalcRecipeID, CalcMaterialID, CalcQuantity) SELECT TOP 1 RecipeID, MaterialID, Quantity FROM @RecipeQuery 
    SET @ROWCOUNT3 = @ROWCOUNT3 + 1 
END 
--OTHERWISE 
ELSE 
BEGIN 
--FIND THE MATERIAL'S RECIPE AND PLACE THE NEW RECIPEID AND MATERIALID INTO TABLE TempMaterialID 
    INSERT INTO @PrevMaterialFinder (PRID, PMID, PQ) SELECT TOP(1) RecipeID, MaterialID, Quantity FROM @RecipeQuery 
    INSERT INTO @MaterialFinder (RID, MID, Q) SELECT (SELECT TOP 1 RecipeID FROM @RecipeQuery), MaterialID, Quantity FROM Recipe WHERE RecipeID = (SELECT TOP 1 MaterialID FROM @RecipeQuery) 

    DECLARE @ROWCOUNT4 INT 
    SET @ROWCOUNT4 = (SELECT COUNT(*) FROM @MaterialFinder) 
    WHILE(@ROWCOUNT2 < @ROWCOUNT4) 
    BEGIN 
     INSERT INTO @CalcMaterial 
     (CalcRecipeID, CalcMaterialID, CalcQuantity) 
     SELECT TOP 1 RID, MID, Q*(SELECT TOP 1 PQ FROM @PrevMaterialFinder)*0.001 
     FROM @MaterialFinder 

     DELETE TOP(1) FROM @MaterialFinder 

     SET @ROWCOUNT2 = @ROWCOUNT2 + 1 
    END 
    SET @ROWCOUNT2 = 0 
END 

SET @ROWCOUNT2 = 0 

--DELETE THE TOP ROW 
DELETE TOP(1) 
FROM @RecipeQuery 

--INSERT THE UPDATED ROW(S) FROM TABLE TempMaterialID INTO TABLE W 
INSERT INTO @TempRecipeQuery 
(TempRecipeID, TempMaterialID, TempQuantity) 
SELECT CalcRecipeID, CalcMaterialID, CalcQuantity 
FROM @CalcMaterial 

--DELETE THE UPDATED ROWS FROM TABLE TempMaterialID 
DELETE FROM @MaterialFinder 
DELETE FROM @PrevMaterialFinder 
DELETE FROM @CalcMaterial 

--INCREASE ROW COUNTER C 
SET @ROWCOUNT1 = @ROWCOUNT1 + 1 

--LOOP ONTO NEXT ROW 
END 
--AT THIS POINT ALL OF THE ROWS HAVE BEEN GONE THROUGH AT LEAST ONCE 

--INSERT THE UPDATED ROWS IN TABLE W INTO THE MAIN TABLE 
INSERT INTO @RecipeQuery 
(RecipeID, MaterialID, Quantity) 
SELECT TempRecipeID, TempMaterialID, TempQuantity 
FROM @TempRecipeQuery 

--TO COMPARE 
--IF THE COUNT OF BOTH TABLES IS THE SAME THEY COULD BE THE SAME 
IF((SELECT COUNT(*) FROM @prevRecipeQuery) = (SELECT COUNT(*) FROM @RecipeQuery)) 
BEGIN 
--IF THE DIFFERENCES BETWEEN THE TWO ARE NULL THEN THEY ARE THE SAME; 
    IF(SELECT MaterialID FROM @RecipeQuery EXCEPT SELECT prevMaterialID FROM @prevRecipeQuery) IS NULL 
--SET DONE TO 1 TO END LOOP 
    SET @isDone = 1 
END 

--DELETE PREV TABLE FOR NEW UPDATE 
DELETE FROM @prevRecipeQuery 

--SET COPY INTO PREV 
INSERT INTO @prevRecipeQuery 
(prevRecipeID, prevMaterialID, prevQuantity) 
SELECT RecipeID, MaterialID, Quantity 
FROM @RecipeQuery 

--SET THE ROW COUNTER BACK TO 0 
SET @ROWCOUNT1 = 0 

--DELETE ALL FROM THE TEMP UPDATED TABLE SO THAT IT CAN BE FILLED WITH UPDATED ROWS IF AGAIN 
DELETE FROM @TempRecipeQuery 

--AT THIS POINT THE LOOP WILL REPEAT IF THERE ARE ANY MORE STOCK SOLUTIONS AS MATERIALS 
END 

--AT THIS POINT, ALL LOOPS ARE DONE AND THE MAIN TABLE DOES NOT CONSIST OF ANY STOCK MATERIALS AS MATERIALS 
--PRINT THE MAIN TABLE 
SELECT RecipeID, MaterialID, Quantity FROM @RecipeQuery 
ORDER BY RecipeID 

入力はレシピテーブル ある出力は、クエリは、各レシピに材料を見つけるために、自分自身を照会として追加された行が黄色で見ることができます テーブルを必要と出力、です。

最後のクエリでは、列と行3のみが表示されなければならないことに注意してください。レベル3の生成方法を示すために追加しました。

Test table

INPUT:
レシピ材料

AA01のB1
AA01のB2
AA01 BB01
BB01 B1
BB01 CC01
CC01 B3
CC01のB4
B1のB1
B2 B2
B3 B3
B4 B4

OUTPUT:
レシピ材料

AA01 B1
AA01 B2
AA01のB1
AA01のB3
AA01のB4
BB01 B1
bb01 B3
bb01 B4
CC01 B3
CC01のB4
B1のB1
B2のB2
B3のB3
B4のB4私はSPでwhileループの性能を改善する方法の

+1

いくつかの管理タスクを除いてループを回避する必要があります。ここには、テーブル変数にデータを挿入するだけで何もしない複数のループがあります。あなたの全体の手順は、単一のクエリに減らすことができると思われます。テーブル全体をテーブル変数にコピーすることによって、この現在のプロシージャ。この全体は、行を苦労させることによる行の代わりに、集合ベースの操作として、最初から完全に再設計する必要があります。 –

+0

私はそれをやろうとしましたが、それを行うために既存のクエリーを継続的にクエリーしなければならず、クエリーしなければならない回数を事前に計算することができませんでした。 – SQLUser

+1

これを行うには再帰が必要です。私たちは2005年からsqlサーバで再帰的なcteを利用できました。現在、あまり一般的ではないことを除いて誰もこのことを行うことができません。すべてのテーブル変数の定義がありますが、開始するサンプルデータや必要な出力はありません。私はこれがセットベースのアプローチで2〜2回実行できると思うが、誰も細部なしでは助けることができない。 –

答えて

1

私の話:私はだった

をwhileループループの約50万行のパフォーマンス低下という同じ問題に直面しています。私はテーブル変数を使用してループしていたし、アイデンティティ列をイテレータとして使用していました。それは遅すぎた。後で、テーブル変数をローカルの一時テーブルに変更し、ループ内の選択されたコマンドに基づいていくつかのインデックスを作成しました。合計時間は30分の早い時間からわずか2〜3分に大幅に減少しました。 その行が多数ある場合は、適切なインデックスを持つtempテーブルを使用してみてください。

短く:テーブル変数を適切なインデックスを持つtempsテーブルに置き換えてください。

+0

あなたは一時テーブル上のインデックスも持つことができます、http://www.sqlteam.com/article/optimizing-performance-indexes-on-temp-tablesしかし、はいインデックスはあなたの友人です、それらを使用してください。 –

+0

私はあなたが何を意味しているのかわからない、変数を一時テーブルに置き換えていますか?そんなことをどうやってやるの? – SQLUser

+0

彼は 'DECLARE TABLE'ではなく' CREATE TABLE' *と 'INSERT INTO TABLE'を意味しますが、一時テーブルにインデックスを追加することもできます –