Условное предложение where приводит к ужасной производительности Firebird

Firebird не знает, как выполнить условный оператор where. Или это то, что я думаю.

Первый запрос возвращает значения через 15 мс.

 SELECT DISTINCT
            A.MANID,
            A.DISNO,
            A.DISID
          FROM
            TABLEB B
            INNER JOIN TABLEA A ON (A.ITEM_ID = B.ITEM_ID)
          WHERE
             (
                (POSITION('%' IN :ISEARCH) = 0 AND B.CATID = :ISEARCH)
             ) 

Этот второй запрос занимает более 40 секунд, и все это касается условия ИЛИ.

 SELECT DISTINCT
            A.MANID,
            A.DISNO,
            A.DISID
          FROM
            TABLEB B
            INNER JOIN TABLEA A ON (A.ITEM_ID = B.ITEM_ID)
          WHERE
             (
                (POSITION('%' IN :ISEARCH) = 0 AND B.CATID = :ISEARCH) OR
                POSITION('%' IN :ISEARCH) <> 0
             )

Как я могу указать Firebird вести себя в подобных ситуациях?


person user1791567    schedule 02.12.2013    source источник
comment
Как выглядит план для каждого из них? Это будет вашим самым большим ключом к тому, что происходит.   -  person nater    schedule 03.12.2013
comment
Первый запрос можно оптимизировать, если есть индекс по CATID, а вот второй будет естественным сканированием. Возможно, вы захотите попробовать два отдельных запроса с UNION ALL и посмотреть, как это работает.   -  person Mark Rotteveel    schedule 03.12.2013
comment
Да, кажется, что Firebird не может обработать второй запрос. Условие ИЛИ немедленно отбрасывает индекс и переходит к полному сканированию таблицы, даже если вы форсируете план. Мне пришлось подходить к моему решению, используя несколько запросов. Спасибо, что ответили на это.   -  person user1791567    schedule 03.12.2013
comment
@user1791567 user1791567 План запроса определяется во время подготовки запроса, в то время оптимизатор не знает, какое значение будет иметь параметр: ISEARCH, поэтому он просто не учитывает индекс, потому что, если применяется второе предложение, он не может используй это.   -  person Mark Rotteveel    schedule 05.12.2013
comment
Что этот SQL пытается сделать? Вы пытаетесь использовать =, если нет %, но использовать SIMILAR TO, если :ISEARCH имеет %? Потому что SIMILAR TO в строке без подстановочного знака должно совпадать с =.   -  person Turophile    schedule 21.01.2014
comment
Попробуйте использовать предложение WITH перед оператором SELECT, чтобы получить необходимые данные из таблицы B, затем удалите предложение WHERE из основного оператора SELECT и посмотрите, решит ли это проблему.   -  person Radix    schedule 15.02.2014


Ответы (1)


Немного надумано, и я не знаком с Firebird, но для этого конкретного случая я бы посоветовал попробовать

(
    (POSITION('%' IN :ISEARCH) = 0 AND B.CATID = :ISEARCH) OR
    POSITION('%' IN :ISEARCH) <> 0
)

быть написано как

(
    (POSITION('%' IN :ISEARCH) <> 0 OR B.CATID = :ISEARCH) 
)

что может иметь больше смысла для оптимизатора запросов? Это по-прежнему OR, и многие СУБД не любят OR, но попробовать стоит...

В худшем случае вы можете попытаться разделить оператор на 2 отдельных запроса, которые вы UNION ALL снова вместе, где один обрабатывает POSITION('%' IN :ISEARCH) <> 0, а другой B.CATID = :ISEARCH. Проблема с этим подходом может заключаться в двойных записях, которые потребуют повторной фильтрации. (он же: новая банка червей...)

person deroby    schedule 29.03.2014
comment
спасибо за ваше предложение. Марк Р. подсказал нам, почему мы не должны использовать это условие в предложении WHERE в Firebird. Кроме того, мы работаем с версией 2.1, в которой отсутствуют некоторые улучшения, внесенные в версию 2.5. Спасибо еще раз. - person user1791567; 01.04.2014