Dobrý den,

prosím o radu nebo jakýkoli nápad v následujícím problému. Řešíme výkonnost 
jednoho SQL dotazu vygenerovaného Hibernatem nad "legacy" databází Oracle 9.

Máme následující 3 tabulky (pro srozumitelnost minimalizováno a přejmenováno, 
ve skutečnosti nejde o osoby):

* ciselnik(id,business_key) - evidence všech číselníků, sloupec business_key je 
unikátní
* ciselnik_data(id,ciselnik_id,hodnota) - data pro jednotlivé číselníky 
sloučená do jedné tabulky, ciselnik_id odkazuje na ciselnik.id
* osoba(id,jmeno,zamestnani_id,pohlavi_id) - osoby, sloupce zamestnani_id a 
pohlavi_id odkazují na ciselnik_data.id, předpokládá se, že referencovaný řádek 
patří ke správnému číselníku.

(Příklad:

ciselnik: 
1 | ZAMESTNANI
2 | POHLAVI

ciselnik_data:
1 | 1 | truhlář
2 | 1 | učitel
3 | 1 | prodavač
4 | 2 | muž
5 | 2 | žena

osoba:
1 | Láďa  | 1 | 4
2 | Lenka | 2 | 5
3 | Lukáš | 2 | 4
)

V Javě je každý číselník modelován jako samostatná třída, tyto třídy mají 
společného předka CiselnikData s anotací 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) a @DiscriminatorColumn, 
samy si jen specifikují hodnotu discriminatoru. Ve třídě Osoba tak mohou být 
přímo property typu Zamestnani a Pohlavi a tyto jsou nepovinné. 

Dosud byla třída CiselnikData mapována na tabulku ciselnik_data a discriminator 
byl provizorně sloupec ciselnik_id. V SQL dotazu pro výběr osoby pak vypadaly 
joiny takto:

from osoba, ciselnik_data ciselnik_data_1, ciselnik_data ciselnik_data_2
where osoba.zamestnani_id = ciselnik_data_1.id(+) and osoba.pohlavi_id = 
ciselnik_data_2.id(+)

Rozlišování číselníků podle id je však nespolehlivé a proto jsme chtěli použít 
k tomu určený sloupec business_key. Vytvořili jsme view ciselnik_view, což je 
join tabulek ciselnik a ciselnik_data. Třídu CiselnikData jsme namapovali na 
toto view, discriminatorem se stal sloupec business_key.

Problém je, že vygenerovaný dotaz je nyní

from osoba, ciselnik_view ciselnik_view_1, ciselnik_view ciselnik_view_2
where osoba.zamestnani_id = ciselnik_view_1.id(+) and osoba.pohlavi_id = 
ciselnik_view_2.id(+)

a exekuční plán tohoto dotazu obsahuje full scan, což v praxi znamená zhoršení 
z 9 na 500ms. Zřejmě inner join ve view do dotazu zatáhl tabulku ciselnik a 
databáze nemá šanci domyslet si, že data pocházejí z jediného číselníku. 

Přišli jsme na to, že full scan zmizí při aplikaci kteréhokoli z opatření:

- doplněním podmínky: and ciselnik_view_1.business_key(+) = 'ZAMESTNANI' and 
ciselnik_view_2.business_key(+) = 'POHLAVI'
- doplněním hintu /*+NO_MERGE*/
- odstraněním outer joinu (jen pro úplnost, vzhledem k  logice aplikace 
nepřijatelné)

Zajímalo by mne, zda by bylo možné vygenerovaný dotaz ovlivnit, aby nejlépe 
dosadil podmínku nebo alespoň doplnil hint. Zkoušeli jsme anotaci 
@ForceDiscriminator na CiselnikData, ale podle všeho se zdá, že nemá dosah 
ovlivnit SQL pro tabulku osoba. Nefungovalo ani použití @Where ve třídě Osoba. 
Nenašel jsem ani nic relevantního na google potažmo stackoverflow.

Díky za jakýkoli postřeh.

Tomáš Záluský

Odpovedet emailem