Является ли порядок условий в предложении where актуальным, если обе стороны остаются объединенными объектами? - PullRequest
0 голосов
/ 30 января 2019

У нас есть два запроса к нашей базе данных, где единственным отличием является порядок условий в предложении OR.

Запросы с одинаковой структурой прекрасно работают с аналогичным набором данных в другой базе данных.Мы работаем с Oracle 12.2.

В таблице Bestand есть две связанные сущности, каждая из которых всегда заполнена.В нашем примере статья связана с KbArticle всегда null.В одном из наших запросов мы получаем все результаты, для которых статья id работает, а в другом случае - нет.Все сущности Bestand выполняют два последних условия (cod_lbr = 12, flg_sperre = 0)

SELECT bestand0_.*
FROM tbl_bestand bestand0_
LEFT OUTER JOIN tbl_kb_artikel customeror1_
ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel
LEFT OUTER JOIN tbl_artikel article2_
ON bestand0_.id_artikel          =article2_.id_artikel
WHERE (customeror1_.id_kb_artikel=3017874 OR article2_.id_artikel          =3017874)
AND bestand0_.cod_lbr            =12
AND NVL(bestand0_.flg_sperre,0)  = 0;

дает результаты Ссылка на объяснительный план

SELECT bestand0_.*
FROM tbl_bestand bestand0_
LEFT OUTER JOIN tbl_kb_artikel customeror1_
ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel
LEFT OUTER JOIN tbl_artikel article2_
ON bestand0_.id_artikel          =article2_.id_artikel
WHERE (article2_.id_artikel          =3017874 OR customeror1_.id_kb_artikel=3017874)
AND bestand0_.cod_lbr            =12
AND NVL(bestand0_.flg_sperre,0)  = 0;

не дает результатов Ссылка на план объяснения

Что не имеет для меня никакого смысла, так как все результаты, которые мы должны получить, соответствуют всем утверждениям в предложении whereв том случае, если вы оставите два последних условия, запрос вернет ожидаемые результаты, поэтому если мы изменим наш запрос на:

SELECT bestand0_.*
FROM tbl_bestand bestand0_
LEFT OUTER JOIN tbl_kb_artikel customeror1_
ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel
LEFT OUTER JOIN tbl_artikel article2_
ON bestand0_.id_artikel          =article2_.id_artikel
WHERE (article2_.id_artikel          =3017874 OR customeror1_.id_kb_artikel=3017874);

, мы получим результаты снова Ссылка на объяснениеПлан

Я ожидаю, что оба запроса дадут одинаковые результаты, я не могу понять, почему порядок условий должен каким-либо образом влиять на результат запроса.У нас здесь есть какие-то индексы?Это должно быть специфично для базы данных, поскольку наши запросы отлично работают в другой базе данных, той же версии, с аналогичным набором данных.

** ОБНОВЛЕНИЕ **

К сожалению, запрос с AND OTHER_ID IS NULL как @kfinity имел ту же проблему, что и запросы выше, что для меня абсолютно бессмысленно.После того, как мы отключили Adaptive Statistics Optimizer , когда @Kuvick упомянул о наших запросах, оба снова получили результаты со следующими планами объяснения:

План объяснения Запрос 1 План объясненияЗапрос 2

Таким образом, отключение оптимизатора решило наши проблемы.

Ответы [ 3 ]

0 голосов
/ 30 января 2019

Глядя на ваши планы объяснения, кажется, что оптимизатор переписывает ваши запросы примерно так:

Первый запрос:

select *
from tbl_bestand b
where ID_KB_ARTIKEL=3017874
  and COD_LBR=12 
  and ID_ARTIKEL IS NOT NULL
  and NVL(b.FLG_SPERRE,0)=0 
union all
select b.*
from tbl_bestand b
left outer join tbl_kb_artikel c
  on b.id_kb_artikel = c.id_kb_artikel
where COD_LBR=12 
  and ID_ARTIKEL=3017874
  and NVL(b.FLG_SPERRE,0)=0 
  and LNNVL(c.ID_KB_ARTIKEL=3017874) -- ie, ID_KB_ARTIKEL <> 3017874 or ID_KB_ARTIKEL is null
;

Второй запрос:

select *
from tbl_bestand b
where ID_ARTIKEL=3017874 
  and COD_LBR=12 
  and ID_KB_ARTIKEL IS NOT NULL -- this is the problem, you didn't want this
  and NVL(b.FLG_SPERRE,0)=0 
union all
select b.*
from tbl_bestand b
left outer join tbl_kb_artikel a
  on b.id_kb_artikel = a.id_kb_artikel
where COD_LBR=12 
  and ID_KB_ARTIKEL=3017874
  and NVL(b.FLG_SPERRE,0)=0 
  and LNNVL(c.ID_ARTIKEL=3017874) -- ie, ID_ARTIKEL <> 3017874 or ID_ARTIKEL is null
;

Таким образом, в каждом случае он избавляется от одного из внешних объединений (поскольку вы не фильтруете другие столбцы во внешних объединяемых таблицах), но переключается в зависимости от того, какое условие вы ставите первым.

Но @scaisEdge прав - если вы видите условия IS NOT NULL, это всегда превращает одно из внешних объединений во внутреннее объединение.Но я думаю, что вы тоже правы, это выглядит как противоречивое поведение оптимизатора.Я не уверен, почему ваша другая база данных не оптимизирует ее таким же образом, но когда я пробую ваш запрос с примерами данных, план объяснения просто получает 2 hash join outer с.

В любом случае, при оставлениисоединяя столбцы в предложении WHERE, я обычно явно запрашиваю NULL, чтобы избежать такого рода вещей.Эта версия работает как положено?

SELECT bestand0_.*
FROM tbl_bestand bestand0_
LEFT OUTER JOIN tbl_kb_artikel customeror1_
ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel
LEFT OUTER JOIN tbl_artikel article2_
ON bestand0_.id_artikel =article2_.id_artikel
WHERE ((article2_.id_artikel=3017874 and bestand0_.id_kb_artikel is null)
    OR (customeror1_.id_kb_artikel=3017874 and bestand0_.id_artikel is null))
AND bestand0_.cod_lbr            =12
AND NVL(bestand0_.flg_sperre,0)  = 0;
0 голосов
/ 31 января 2019

Мы определили проблему как настройку Oracle OPTIMIZER_ADAPTIVE_PLANS и OPTIMIZER_ADAPTIVE_STATISTICS.По умолчанию после настройки БД первый активен, а второй деактивирован.

В нашем случае оба были активированы.Сразу после деактивации OPTIMIZER_ADAPTIVE_STATISTICS (т. Е. Возврата к настройкам по умолчанию) оба запроса дали одинаковые результаты (как и следовало ожидать).Эти настройки можно проверить с помощью следующего запроса:

SELECT * FROM v$parameter WHERE 1 = 1 AND LOWER(name) LIKE LOWER('optimizer_ad%') ORDER BY 1 DESC;
0 голосов
/ 30 января 2019

Не следует использовать столбцы левых таблиц соединения, в которых условие, в противном случае эта работа, используется как внутреннее соединение, поэтому соответствующее условие должно быть помещено в предложение ON

    SELECT bestand0_.*
    FROM tbl_bestand bestand0_
    LEFT OUTER JOIN tbl_kb_artikel customeror1_ 
        ( ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel 
                 OR customeror1_.id_kb_artikel=3017874 )
    LEFT OUTER JOIN tbl_artikel article2_
        ( ON bestand0_.id_artikel =article2_.id_artikel 
                  OR  article2_.id_artikel =3017874 )
    WHERE  bestand0_.cod_lbr            =12
    AND NVL(bestand0_.flg_sperre,0)  = 0;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...