Requête comportant des jointures externes se comporte différemment dans Oracle 12c

j'ai eu un problème concernant des données manquantes sur Oracle 12c.

j'ai regardé le code et j'ai trouvé une requête qui fonctionne sur mysql, mssql, oracle 11g, mais qui a un comportement différent dans oracle 12c.

j'ai généralisé la structure de la table et de la question un peu et reproduit la question.

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');

lancer la requête:

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)

sur Oracle 11g donne (ici un Violon SQL ):

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)

pourtant le même schéma, données, et requête sur 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)

il semble que les deux deuxièmes jointures externes ne ramènent rien parce qu'il n'y a pas de ligne dans "thing_related" à rejoindre. Cependant je ne comprends pas pourquoi la jointure externe ne renvoie pas nulls dans ce cas comme il le fait dans Oracle 11g, Mysql, etc..

j'ai fait des recherches et j'ai trouvé de la documentation sur L'Oracle 12c a eu un certain nombre d'améliorations pour les jointures externes, mais rien qui a mis en évidence un changement qui aurait une incidence sur ce.

est-ce que n'importe qui sait pourquoi cela se produit seulement pour Oracle 12c, et comment mieux je réécrirais ceci pour travailler dans 12c et maintenir la compatibilité avec 11g, mysql, etc.?

EDIT: Attached plans.

Oracle 11g:

enter image description here

Oracle 12c:

enter image description here

14
demandé sur Jon Heller 2013-10-30 18:53:59

2 réponses

mise à JOUR: C'est corrigé dans la et 12.1.0.2;.


cela ressemble définitivement à un bug dans 12.1.0.1. Je vous encourage à créer une demande de service à travers Oracle support. Ils pourraient être en mesure de trouver une solution ou un meilleur travail. Et avec un peu de chance, Oracle pourra le réparer dans une version future pour tout le monde. Normalement, le pire dans le travail avec support est de reproduire le problème. Mais puisque vous avez déjà un très bon test cas, ce problème peut être facile à résoudre.

il y a probablement plusieurs façons de contourner ce bogue. Mais il est difficile de dire quelle méthode sera toujours. Votre requête réécrire peut fonctionner maintenant, mais si les statistiques d'optimiseur changent peut-être le plan changera à l'avenir.

une autre option qui fonctionne pour moi sur 12.1.0.1.0 est:

ALTER SESSION SET optimizer_features_enable='11.2.0.3';

mais vous devez vous rappeler de toujours changer ce paramètre avant que la requête ne soit exécuter, puis le modifier à '12.1.0.1' après. Il y a des façons d'intégrer cela dans un indice de requête, comme /*+ OPT_PARAM('optimizer_features_enable' '11.2.0.3') */ . Mais pour une raison quelconque, ça ne marche pas ici. Ou peut-être Pouvez-vous Temporairement régler cela pour l'ensemble du système et le changer en arrière après une correction ou mieux travailler autour est disponible.

quelle que soit la solution que vous utilisez, n'oubliez pas de la documenter. Si une requête est impair le prochain développeur peut essayer de "réparer" et de frapper le même problème.

12
répondu Jon Heller 2016-02-22 19:53:08

se référer à:

ANSI Outer Join Query renvoie des résultats erronés après la mise à niveau vers 12.1.0.1 (Doc ID 1957943.1)

Bogue non publié 16726638

fixe dans 12.1.0.2 (j'ai testé ceci)

solution de contournement (j'ai testé en 12.1.0.1):

alter session set "_optimizer_ansi_rearchitecture"=false;          

la Note 1957943.1 recommande comme alternative:

optimizer_features_enable = '11.2.0.4';

mais qui fait pas travail.

4
répondu Brian Fitzgerald 2015-02-14 20:16:44