Запрос Firebird занимает слишком много времени при использовании предложения IN с хранимой процедурой - PullRequest
0 голосов
/ 28 февраля 2019

Для отчета мне пришлось написать рекурсивную хранимую процедуру GET_RECIPE_STEPS_ID(recipe_id).Он возвращает идентификаторы шагов, которые имеют тип рецепта.IE

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,038 с)

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^

1 Ответ

0 голосов
/ 28 февраля 2019

Проблема двоякая:

  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 встречается несколько раз в выходных данных хранимой процедуры, теперь она также выдаст несколько строк.Возможно, вам придется настроить соответственно.

...