Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Witajcie.
Sprawa przedstawia się następująco - jedno z zapytań wykonuje mi się za długo, i muszę je jakoś zoptymalizować. To zapytanie nie zostało napisane przeze mnie, dlatego za wszystkie sugestie co do poprawności/szybkości konkretnych części będę bardzo wdzięczny.
Nie będę na razie wklejać zapytania, tylko postaram się opisać problem.

Moje zapytanie odpytuje perspektywę. Ta perspektywa odpytuje kolejną perspektywę, która korzysta z kilku tabelek. Jedna z nich jest dosyć duża (ok 20 mln wierszy) - ma wszystkie potrzebne indeksy i została spartycjonowana po polu z pewną datą (data_we). Czyli :

moje zapytanie
|
|
perspektywa 2 (kilka obliczeń)
|
|
perspektywa 1 (kilka operacji czyszczących)
|
|
Tabela_historyczna (joiny z mniejszymi tabelkami,partycje na data_we)

Gdybym ja to robił od początku - pewnie zbudowałbym to inaczej, jednak jest jak jest i raczej rzeczy z tych perspektyw, jak i samej struktury ruszać nie mogę.
Dochodząc do meritum, w zapytaniu znajduje się następujący warunek :
where
data_we between data1 and data2
or
(data_wy between data1 and data2 and data_we < data2).

W jaki sposób można to usprawnić? Dla pierwszego warunku przeglądam odpowiednie partycje - dla drugiego - przeglądam zdecydowaną większość partycji. Czy da się coś z tym zrobić?
Próbowałem rozbić ten warunek na dwa niezależne selecty połączone unionem, ale zapytanie działało jeszcze dłużej.
Zauważyłem, że gdy pojawi się taki warunek - szybciej działa przejrzenie całej tabeli nie uwzględniając partycji. Może przeglądając plan wykonania zapytania trochę więcej będziecie potrafili pomóc :


SELECT STATEMENT, GOAL = ALL_ROWS 156 1 124
SORT GROUP BY 1 124
PX COORDINATOR
PX SEND QC (RANDOM) SYS :TQ10006
BUFFER SORT 1 124
CONCATENATION
NESTED LOOPS OUTER 147 1 124
HASH JOIN OUTER 147 1 120
PX RECEIVE 141 1 104
PX SEND HASH SYS :TQ10002 141 1 104
BUFFER SORT 1 124
PX PARTITION RANGE ITERATOR 141 1 104
TABLE ACCESS BY LOCAL INDEX ROWID ATOSTEST NEW_HIST_MAIN_GD 141 1 104
INDEX RANGE SCAN ATOSTEST HIS_L_INDEX_GD 58 4149
PX RECEIVE 5 17099 273584
PX SEND HASH SYS :TQ10003 5 17099 273584
BUFFER SORT 1 124
VIEW SYS 5 17099 273584
HASH JOIN RIGHT OUTER 5 17099 923346
PX RECEIVE 2 93 1581
PX SEND BROADCAST SYS :TQ10000 2 93 1581
BUFFER SORT 1 124
VIEW SYS 2 93 1581
NESTED LOOPS 2 93 2232
PX BLOCK ITERATOR
TABLE ACCESS FULL CMM_DB CMM_EQP_TYPE_REVISION 2 93 1860
INDEX UNIQUE SCAN CMM_DB CMM_VENDOR_PK 0 1 4
PX BLOCK ITERATOR 3 17099 632663
TABLE ACCESS FULL NEW_CONFIG_DB EQP_NAME 3 17099 632663
INDEX UNIQUE SCAN NEW_CONFIG_DB PROBABLE_CAUSE_PK 0 1 4
NESTED LOOPS OUTER 10 1 124
HASH JOIN OUTER 10 1 120
PX RECEIVE 4 1 104
PX SEND HASH SYS :TQ10004 4 1 104
BUFFER SORT 1 124
PX PARTITION RANGE ITERATOR 4 1 104
TABLE ACCESS BY LOCAL INDEX ROWID ATOSTEST NEW_HIST_MAIN_GD 4 1 104
INDEX RANGE SCAN ATOSTEST HIS_DATETIME_UP_INDEX2_GD 1 89
PX RECEIVE 5 17099 273584
PX SEND HASH SYS :TQ10005 5 17099 273584
BUFFER SORT 1 124
VIEW SYS 5 17099 273584
HASH JOIN RIGHT OUTER 5 17099 923346
PX RECEIVE 2 93 1581
PX SEND BROADCAST SYS :TQ10001 2 93 1581
BUFFER SORT 1 124
VIEW SYS 2 93 1581
NESTED LOOPS 2 93 2232
PX BLOCK ITERATOR
TABLE ACCESS FULL CMM_DB CMM_EQP_TYPE_REVISION 2 93 1860
INDEX UNIQUE SCAN CMM_DB CMM_VENDOR_PK 0 1 4
PX BLOCK ITERATOR 3 17099 632663
TABLE ACCESS FULL NEW_CONFIG_DB EQP_NAME 3 17099 632663
INDEX UNIQUE SCAN NEW_CONFIG_DB PROBABLE_CAUSE_PK 0 1 4



Wrzucę jednak trochę więcej informacji :
Perspektywa 1 (NEW_HIST_MAIN_gd - największa tabela):

CREATE OR REPLACE VIEW HISTORY_DB.VW_ALARM_MAIN_2 AS
SELECT /*PARALLEL(ALARM) */
--jakies normalne kolumny
FROM atostest.NEW_HIST_MAIN_gd ALARM
LEFT OUTER JOIN NEW_CONFIG_DB.PROBABLE_CAUSE PROBC ON (ALARM.HISM_PROBABLE_CAUSE = PROBC.PROBABLE_CAUSE_ID)
LEFT OUTER JOIN (NEW_CONFIG_DB.EQP_NAME EQP
LEFT OUTER JOIN (CMM_DB.CMM_EQP_TYPE_REVISION ETYPE
INNER JOIN CMM_DB.CMM_VENDOR VEN ON (ETYPE.VENDOR_ID = VEN.VENDOR_ID))
ON (EQP.EQP_TYPE_NAME = ETYPE.EQP_TYPE_NAME))
ON (ALARM.HISM_EQP_NAME = EQP.EQP_NAME);


Perspektywa 2 :

CREATE OR REPLACE VIEW HISTORY_DB.VW_FAM_REPORTER_2 AS
SELECT /*+ PARALLEL(A) */
-- normalne rzeczy + trochę case'ów -> już pracuję nad optymalizacją
FROM
history_db.VW_ALARM_MAIN_2 A
WHERE
A.HISM_REPEATED_COUNT= 1



Zapytanie :


SELECT /*+ PARALLEL(r2) */
--normalny select
FROM
HISTORY_DB.VW_FAM_REPORTER_2 r2
WHERE

(r2.HISM_DATETIME_UP BETWEEN '2010-03-26 00:00:00' AND '2010-03-28 00:00:00'
OR
(r2.HISM_DATETIME_UP <= '2010-03-28 00:00:00' and r2.HISM_DATETIME_DOWN BETWEEN '2010-03-26 00:00:00' AND '2010-03-28 00:00:00'))
-- pozostałe warunki nie sprawiają problemów


Partycje zrobione na polu hism_datetime_up
Z góry dzięki za wszelką pomocGrzegorz Drzymała edytował(a) ten post dnia 26.04.10 o godzinie 22:11
Adam Orlik

Adam Orlik Starszy
Administrator Baz
Danych Oracle

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Proponuję zacząć od wyeliminowania full scanów na kolumnach CMM_DB.CMM_EQP_TYPE_REVISION i NEW_CONFIG_DB.EQP_NAME. Czy są na nich założone indeksy? Jeżeli tak to kiedy ostatni raz były przebudowane / przeliczone statystyki na tych indeksach?
Krzysztof Bielecki

Krzysztof Bielecki Senior consultant,
Capgemini Polska

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

do czego służy to zapytanie ?
ile sie wykonuje ?
i ile powinno sie wykonywac po optymalizacji ?
Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Odpowiadajac zbiorowo :

1. Indeksy na tych tabelkach nie pomagaja - tabele sa male (pod wzgledem ilosci wierszy i kolumn) - optymalizator woli i tak zrobic full scan

2. Zapytanie ma wyciagnac z tabeli historycznej pewne zdarzenia, posumowac, wyznaczyc srednia, mediane itp, dla roznych typow urzadzen.

Wykonuje sie za dlugo.
Caly raport (pokazalem teraz tylko czesc) powinien wykonac sie w 10 minut. Pokazana czesc dla pewnego przedzialu czasowego szla oryginalnie 8 minut. Teraz udalo mi sie uzyskac niecale 2 minuty.
Krzysztof Bielecki

Krzysztof Bielecki Senior consultant,
Capgemini Polska

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

to chyba juz zadowalajaco ;)
Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

No wlasnie niekoniecznie :( to jest jedynie czesc raportu. Ale z tego co zauwazylem, mozna ten raport przebudowac, i wtedy czas generowania calego raprotu powinien byc krotszy, a baza mniej zajechana ;)

Co do tego warunku, ktory zostal podany w pierwszym poscie - to juz nieaktualne. Autor zapytania "sie pomylil"...mial byc and zamiast or :]

Inna watpliwosc - czy wiecie moze, czemu Oracle nie korzysta z partycji np w takim prostym warunku
select * from tabela where pole_klucz_partycji between data and data?
Dla przedzialu 1-2 dni partycje sa wykorzystywane, wyzej juz niekoniecznie.
Aha - partycja dzielona zakresem jednego dnia

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Inna watpliwosc - czy wiecie moze, czemu Oracle nie korzysta z partycji np w takim prostym warunku
select * from tabela where pole_klucz_partycji between data and data?
Dla przedzialu 1-2 dni partycje sa wykorzystywane, wyzej juz niekoniecznie.
Aha - partycja dzielona zakresem jednego dnia

Bo obliczył, że mu się to nie opłaca.
Co to w ogóle znaczy, że nie korzysta z partycji?
Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

tzn, ze znika mi ten kawalek


PX PARTITION RANGE ITERATOR 4 1 104

TABLE ACCESS BY LOCAL INDEX ROWID ATOSTEST NEW_HIST_MAIN_GD 4 1 104



i generowany jest inny plan zapytania
np taki

SELECT STATEMENT, GOAL = ALL_ROWS 250 311 36387
PX COORDINATOR PX SEND QC (RANDOM) SYS :TQ10004 250 311 36387
SORT GROUP BY 250 311 36387
PX RECEIVE 249 311 36387
PX SEND HASH SYS :TQ10003 249 311 36387
BUFFER SORT 250 311 36387
FILTER NESTED LOOPS OUTER 249 311 36387
FILTER HASH JOIN OUTER 249 311 35143
PX RECEIVE 243 764 58828
PX SEND HASH SYS :TQ10001 243 764 58828
PX BLOCK ITERATOR 243 764 58828
TABLE ACCESS FULL ATOSTEST NEW_HIST_MAIN_GD 243 764 58828
PX RECEIVE 5 17154 617544
PX SEND HASH SYS :TQ10002 5 17154 617544
VIEW SYS 5 17154 617544
HASH JOIN RIGHT OUTER 5 17154 909162
PX RECEIVE 2 93 1581
PX SEND BROADCAST SYS :TQ10000 2 93 1581
VIEW SYS 2 93 1581
NESTED LOOPS 2 93 2232
PX BLOCK ITERATOR TABLE ACCESS FULL CMM_DB CMM_EQP_TYPE_REVISION 2 93 1860
INDEX UNIQUE SCAN CMM_DB CMM_VENDOR_PK 0 1 4
PX BLOCK ITERATOR 3 17154 617544
TABLE ACCESS FULL NEW_CONFIG_DB EQP_NAME 3 17154 617544
INDEX UNIQUE SCAN NEW_CONFIG_DB PROBABLE_CAUSE_PK 0 1 4



Czyli rozumiem, ze partycje nie sa przycinane, tylko leci po calej tabeli.
Dla mnie jest to dziwne, poniewaz patrze na zakres 2 dni (2 partycje), wiec chyba logiczne bylo by skorzystanie z dokladnie tych partycji, ktorych potrzebujemy, niz przegladanie wszystkich ...
Jacek Ołowiak

Jacek Ołowiak kierownik zespołu IT

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

jeśli oracle wymyslił inny plan to znaczy, że sobie coś obliczył. Możesz wymusić inny sposób komentarzem np.... hmmm /*+ index(klucz)*/*

konto usunięte

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

A ile masz tych partycji? Skoro przy zapytaniu z zakresu > 2 dni probuje robic full table scan, to pewnie mysli ze partycji jest kilkanascie ;)

Sprawdz, czy masz poprawnie policzone statystyki dla tej tabeli.

Mozesz wkleic tez plan zapytania bez parallel - bedzie czytelniejszy.
Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Ireneusz Ptak:
A ile masz tych partycji? Skoro przy zapytaniu z zakresu > 2 dni probuje robic full table scan, to pewnie mysli ze partycji jest kilkanascie ;)

Sprawdz, czy masz poprawnie policzone statystyki dla tej tabeli.

Mozesz wkleic tez plan zapytania bez parallel - bedzie czytelniejszy.

Partycji jest ok 90. Cała tabela może mieć do ok 80 milionów wierszy. W tej chwili jest jednak ok 18 milionów, jednak jest to chyba ilość, która zachęca do wykorzystania partycji ;)
Jutro sprawdzę jeszcze statystyki.
Tak czy inaczej, czasy generowania raportów są zadowalające, więc problem można uznać za rozwiązany.
Grzegorz D.

Grzegorz D. PL/SQL Developer

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Wiec tak :
Statystyki przeliczone, problem z partycjami dalej wystepuje.
Obrazkowo.

Zapytanie :

select * from new_hist_main_gd where
datetime_up between to_timestamp('2010-02-02 00:00:00.000') and to_timestamp('2010-02-03 00:00:00.000')

Plan :

SELECT STATEMENT, GOAL = ALL_ROWS 23869 129035 111228170
PX COORDINATOR
PX SEND QC (RANDOM) SYS :TQ10000 23869 129035 111228170
FILTER
PX PARTITION RANGE ITERATOR 23869 129035 111228170
TABLE ACCESS BY LOCAL INDEX ROWID ATOSTEST NEW_HIST_MAIN_GD 23869 129035 111228170
INDEX RANGE SCAN ATOSTEST HIS_DATETIME_UP_INDEX_1_GD 46 129035 begin_of_the_skype_highlighting              46 129035      end_of_the_skype_highlighting


Zapytanie :

select * from new_hist_main_gd where
datetime_up between to_timestamp('2010-02-02 00:00:00.000') and to_timestamp('2010-02-05 00:00:00.000')

Plan :

SELECT STATEMENT, GOAL = ALL_ROWS 76402 454140 391468680
PX COORDINATOR
PX SEND QC (RANDOM) SYS :TQ10000 76402 454140 391468680
FILTER
PX BLOCK ITERATOR 76402 454140 391468680
TABLE ACCESS FULL ATOSTEST NEW_HIST_MAIN_GD 76402 454140 391468680



Tabela spartycjonowana po polu datetime_up, ktore jest typu timestamp(3).
Wiecie moze czemu tak sie dzieje?
Mozna przyjac, ze liczba rekordow w partycji jest w miare rowna (140-150 tysiecy).
Parallel degree = 5

Dzieki za wszelkie sugestieGrzegorz Drzymała edytował(a) ten post dnia 01.05.10 o godzinie 20:03

konto usunięte

Temat: [Oracle 10g] Optymalizacja zapytania z "OR"

Eeeee... tu problem jest w czyms innym :)

W tym "gorszym" przypadku Oracle wcale nie probuje robic FTSa na tabeli - w takiej sytuacji mialbys PARTITION RANGE ALL w planie zapytania ;)

Trzeba sie tylko zastanowic, dlaczego w jednym przypadku korzysta z indeksu, a w drugim nie.

W dalszym ciagu obstawiam, ze to cos nie tak ze statystykami - na pewno w ALL_TAB_PARTITIONS i ALL_IND_PARTITIONS jest wszystko OK?

Z drugiej strony domyslam sie, ze HIS_DATETIME_UP_INDEX_1_GD masz zalozony na DATETIME_UP. Jezeli rzeczywiscie wykonujesz duzo zapytan z niewielkiego zakresu czasu, to moze ma to jakis sens, ale dla jednego zapytania raczej nie warto tworzyc takiego indeksu.



Wyślij zaproszenie do