使用带有存储过程的IN子句时,Firebird查询耗时太长

问题描述 投票:2回答:1

对于一份报告,我不得不写一个递归的存储过程GET_RECIPE_STEPS_ID(recipe_id)。它返回类型为recipe的步骤ID。 I.E.

SELECT GET_RECIPE_STEPS_ID.ID FROM GET_RECIPE_STEPS_ID(3189)

It Returns
3189
3190
3191
3192

当我自己运行它时它很快(比如0.031秒的执行时间)。但是如果要在查询中使用IN子句则需要很长时间。像下面的查询花了差不多12分钟。

SELECT rs.RECIPEID
FROM RECIPESTEPS rs
WHERE rs.RECIPEID IN (select GET_RECIPE_STEPS_ID.ID from GET_RECIPE_STEPS_ID(3189))

这相当于以下查询,几乎和存储过程本身一样快(0.038sec)

Select rs.RECIPEID
FROM RECIPESTEPS rs
WHERE rs.RECIPEID IN (3189, 3190, 3191, 3192)

存储过程

CREATE OR ALTER PROCEDURE GET_RECIPE_STEPS_ID 
 (recipe_id integer) 
RETURNS 
 (id integer)
AS 
declare variable coType integer;
BEGIN
  /* Recursive Procedure 
   * For Passed Recipe 'Recipe_id', it Returns the step's which are of type Recipe again.
   * 
   * If any step is of type Recipe(i.e COTYPE = 1)
   * Then it calls itself again for that step(Recipe) 
   */
    id =: recipe_id;
    SUSPEND;

    FOR SELECT rs.COMMODITYID, c.COTYPE
        FROM RECIPESTEPS rs 
        LEFT JOIN COMMODITIES c ON c.COMMODITYID = rs.COMMODITYID
        WHERE rs.RECIPEID  =: recipe_id INTO :id, :coType
    Do
    BEGIN
        IF(coType = 1)
        THEN 
            FOR SELECT r.RECIPEID FROM RECIPES r WHERE r.LATEST = 1 AND r.COMMODITYID =:id into :id
            DO
            BEGIN
                FOR SELECT GET_RECIPE_STEPS_ID.ID
                    FROM GET_RECIPE_STEPS_ID(:id) INTO :id
                DO
                BEGIN
                    SUSPEND;
                END
            END     
    END 
END^
sql firebird
1个回答
2
投票

问题是双重的:

  1. IN开始并没有很好的表现
  2. 在这种情况下,存储过程将针对每一行执行,而不是像您期望的那样执行;我猜Firebird优化器并没有推断出这个存储过程调用与查询没有关联。

如果您将查询转换为使用INNER JOIN而不是IN,它可能会表现得更好:

select rs.RECIPEID
from GET_RECIPE_STEPS_ID(3189) grs
inner join RECIPESTEPS rs
  on rs.RECIPEID = grs.ID

我假设您的真实查询可能更复杂,因为否则只需select ID from GET_RECIPE_STEPS_ID(3189)即可。

上面的查询将与IN略有不同,例如,如果ID在存储过程输出中多次出现,它现在也会产生多行。您可能需要相应调整。

© www.soinside.com 2019 - 2024. All rights reserved.