Подтвердить что ты не робот

Запрос с внешними объединениями ведет себя по-разному в Oracle 12c

У меня возникла проблема с отсутствием данных Oracle 12c.

Я просмотрел код и нашел запрос, который работает на mysql, mssql, oracle 11g, но имеет другое поведение в oracle 12c.

Я обобщил структуру и запрос таблицы и воспроизвел проблему.

create table thing (thing_id number, display_name varchar2(500));
create table thing_related (related_id number, thing_id number, thing_type varchar2(500));
create table type_a_status (related_id number, status varchar2(500));
create table type_b_status (related_id number, status varchar2(500));

insert into thing values (1, 'first');
insert into thing values (2, 'second');
insert into thing values (3, 'third');
insert into thing values (4, 'fourth');
insert into thing values (5, 'fifth');
insert into thing_related values (101, 1, 'TypeA');
insert into thing_related values (102, 2, 'TypeB');
insert into thing_related values (103, 3, 'TypeB');
insert into thing_related (related_id, thing_id) values (104, 4);

insert into type_a_status values (101, 'OK');
insert into type_b_status values (102, 'OK');
insert into type_b_status values (103, 'NOT OK');

Запуск запроса:

SELECT t.thing_id AS id, t.display_name as name,
       tas.status as type_a_status,
       tbs.status as type_b_status
FROM thing t LEFT JOIN thing_related tr 
  ON t.thing_id = tr.thing_id
LEFT JOIN type_a_status tas 
  ON (tr.related_id IS NOT NULL 
      AND tr.thing_type = 'TypeA' 
      AND tr.related_id = tas.related_id)
LEFT JOIN type_b_status tbs 
  ON (tr.related_id IS NOT NULL 
      AND tr.thing_type = 'TypeB' 
      AND tr.related_id = tbs.related_id)

на Oracle 11g дает (здесь SQL Fiddle):

ID | NAME   | TYPE_A_STATUS | TYPE_B_STATUS
 1 | first  |            OK | (null)
 2 | second |        (null) | OK
 3 | third  |        (null) | NOT OK
 4 | fourth |        (null) | (null)
 5 | fifth  |        (null) | (null)

Тем не менее та же схема, данные и запрос на Oracle 12c:

ID | NAME   | TYPE_A_STATUS | TYPE_B_STATUS
 1 | first  |            OK | (null)
 2 | second |        (null) | OK
 3 | third  |        (null) | NOT OK
 4 | fourth |        (null) | (null)

Кажется, что два вторых внешних объединения ничего не могут вернуть, потому что в "thing_related" нет строки. Однако я не понимаю, почему внешнее объединение не возвращает nulls в этом случае, как это происходит в Oracle 11g, Mysql и т.д.

Я изучал и обнаружил документацию, в которой Oracle 12c имел ряд улучшений для внешних соединений, но ничего, что указывало на изменение, которое повлияло бы на это.

Кто-нибудь знает, почему это происходит только для Oracle 12c, и как лучше всего переписать это для работы в 12c и поддерживать совместимость с 11g, mysql и т.д.

EDIT: прилагаемые планы.

Oracle 11g:

enter image description here

Oracle 12c:

enter image description here

4b9b3361

Ответ 1

UPDATE: Это исправлено в 12.1.0.2.


Это определенно похоже на ошибку в 12.1.0.1. Я бы посоветовал вам создать запрос на обслуживание через поддержку Oracle. Они могут найти исправление или улучшить работу. И, надеюсь, Oracle может исправить это в будущей версии для всех. Обычно худшая часть работы с поддержкой - это проблема. Но поскольку у вас уже есть очень хороший тестовый пример, эта проблема может быть легко разрешена.

Вероятно, есть много способов обойти эту ошибку. Но трудно сказать, какой метод всегда будет работать. Повторная запись запроса может работать сейчас, но если статистика оптимизатора изменится, возможно, план изменится в будущем.

Другой вариант, который работает для меня на 12.1.0.1.0:

ALTER SESSION SET optimizer_features_enable='11.2.0.3';

Но вам нужно помнить, чтобы всегда изменять этот параметр перед запуском запроса, а затем после этого изменить его на "12.1.0.1". Есть способы вставить это в подсказку запроса, например /*+ OPT_PARAM('optimizer_features_enable' '11.2.0.3') */. Но по какой-то причине это не работает. Или, возможно, вы можете временно установить это для всей системы и изменить ее после исправления или улучшения работы.

Какое бы решение вы ни использовали, не забудьте его документировать. Если запрос выглядит странно, следующий разработчик может попытаться "исправить" его и решить ту же проблему.

Ответ 2

Обратитесь к:

ANSI Outer Join Query возвращает неправильные результаты после обновления до 12.1.0.1 (Doc ID 1957943.1)

Неопубликованная ошибка 16726638

Исправлено в 12.1.0.2 (я протестировал это)

Обходной путь (я тестировал в 12.1.0.1):

alter session set "_optimizer_ansi_rearchitecture"=false;          

Примечание 1957943.1 рекомендует в качестве альтернативы:

optimizer_features_enable = '11.2.0.4';

но это не работает.

Ответ 3

У меня запланирован переход с 11gR2 на 12c, и весь синтаксис в ANSI. Это действительно кошмар, чтобы проверить каждый запрос и сравнить его с данными 11g. Установлен ли параметр alter session set "_optimizer_ansi_rearchitecture" = false; это единственное решение или ошибка исправлена