循环存储过程太慢

时间:2017-11-28 18:20:20

标签: sql sql-server database loops ms-access

这里我有一个存储过程似乎比我希望的运行时间更长一些。使这个算法如此冗长的原因是在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

输出:
食谱材料

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

1 个答案:

答案 0 :(得分:1)

我的故事是关于如何改善SP中while循环的性能:

我遇到了同样的问题,即while循环的性能缓慢,几乎有五十万行。我使用表变量循环并使用标识列作为迭代器。太慢了。后来我将table变量更改为本地临时表,并根据循环内的select命令创建了一些索引。从30分钟的早期开始,总时间急剧减少到2-3分钟。 如果您还有那么多行,请尝试使用具有适当索引的临时表。

简而言之:尝试使用具有适当索引的临时表替换表变量。