ad2. explain plan set statement_id = 'p1' for select nazwa, count(*) from zesp natural join prac group by nazwa order by count(*) desc; >Wyjasniono plan select operation, object_name, id, cost, parent_id from plan_table where statement_id = 'p1' order by id; > --------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | --------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | SORT ORDER BY | | | | | | 2 | SORT GROUP BY | | | | | | 3 | MERGE JOIN | | | | | | 4 | SORT JOIN | | | | | | 5 | TABLE ACCESS FULL| PRAC | | | | |* 6 | SORT JOIN | | | | | | 7 | TABLE ACCESS FULL| ZESP | | | | --------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 6 - access("ZESP"."ID_ZESP"="PRAC"."ID_ZESP") filter("ZESP"."ID_ZESP"="PRAC"."ID_ZESP") Note: rule based optimization Zostal uzyty optymalizator regulowy poniewaz nie mamy zadnych statystyk. Na tabeli zesp oraz prac nie mamy zadnych indeksow totez uzyto polaczenia merge join, tabele zostaly najpierw posortowane wedlug atrybutow polaczeniowych. Potem sorotwanie dla group by i order by. ad3. set autotrace on explain; set timing on; > Całkowity: 00:00:00.02 Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 COLLECTION ITERATOR (PICKLER FETCH) OF 'DISPLAY' ad4. // tylko sledzenie planu set autotrace traceonly explain; lub // sledzenie planu oraz wyniki zapytania set autotrace on explain; select rowid, nazwisko, plec, placa from prac where id_prac = 900; 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (FULL) OF 'PRAC' Wybrano optymalizator regulowy gdyz nie mamy statystyk dla tabeli prac. Dostep do tabeli odbyl sie za pomoca full scan gdyz brak indeksu powoduje ze trzeba przejrzec cala tabele. ad5. set autotrace on explain; select rowid, nazwisko, plec, placa from prac where rowid = 'ABLxafAASAAAKPmAC8'; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY USER ROWID) OF 'PRAC' Dostep do tabeli odbyl sie przez adresu rekordu, poniewaz zostal on podany. ad6. set autotrace off; select * from user_indexes where table_name = 'PRAC'; > brak indeksow ad7. create index prac_idx on prac(id_prac); set autotrace on explain; select rowid, nazwisko, plec, placa from prac where id_prac = 900; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 2 1 INDEX (RANGE SCAN) OF 'PRAC_IDX' (NON-UNIQUE) Dostep do tabeli jest za pomoca rowid poniewaz korzystamy z indexu na id_prac. Poniewaz index prac_idx nie jest unikalny mamy dostep do indeksu poprzez range scan gdyz w danym lisciu moze byc kilka rowid. Gdyby indeks byl unikalny mielibysmy dostep do indexu unique scan. ad8. set autotrace off; drop index prac_idx; alter table prac add constraint prac_pk PRIMARY KEY(id_prac); ad9. set autotrace off; select * from user_indexes where table_name = 'PRAC'; select * from user_ind_columns where table_name = 'PRAC'; widzimy ze na kluczu glownym zostal automatycznei zbudowany unikalny indeks ad10. set autotrace on explain; select rowid, nazwisko, plec, placa from prac where id_prac = 900; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 2 1 INDEX (UNIQUE SCAN) OF 'PRAC_PK' (UNIQUE) Poniewaz mamy indeks unikalny na id_prac dostep do indeksu jest poprzez unique scan. ad11. set autotrace off; create index prac_nazwisko_idx on prac(nazwisko); set autotrace on explain; select * from prac where nazwisko = 'Prac155'; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 2 1 INDEX (RANGE SCAN) OF 'PRAC_NAZW ISKO_IDX' (NON-UNIQUE) Optymalizator korzysta z indeksu na nazwisku, poniewaz jest on nieunikalny to range scan. select * from prac where nazwisko like 'Prac155%'; > 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 2 1 INDEX (RANGE SCAN) OF 'PRAC_NAZW ISKO_IDX' (NON-UNIQUE) Optymalizator korzysta z indeksu na nazwisku, poniewaz warunek where ma % na koncu, totez zastosowanie indeksu jest mozliwe. Dostep range scan do indeksu poniewaz indeks zostanie przejrzany do poziomu Prac155 a potem wszystko musi zostac odczytane. select * from prac where nazwisko like '%Prac155%'; > 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (FULL) OF 'PRAC' Pelny przeglad tabeli poniewaz nie mozna zastosowac indeksu ze wzgledu na to iz na poczatku warunku where mamy %. Niemozliwe jest uzycie indeksu poniewaz mamy w nim kolejnosc leksykograficzna, a znak % na poczatku uniemozliwia nam zakwalifikowanie wartosci warunku. select * from prac where nazwisko like 'Prac155%' or nazwisko like 'Prac255%'; > 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 CONCATENATION 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 3 2 INDEX (RANGE SCAN) OF 'PRAC_NAZWISKO_IDX' (NON-UNIQUE) 4 1 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 5 4 INDEX (RANGE SCAN) OF 'PRAC_NAZWISKO_IDX' (NON-UNIQUE) Optymalizator skorzystal dwa razy z indeksu na nazwisku, dostep range scan poniewaz nie jst unikalny. W obu przypadkach uzyskal wartosci rowid, poczym odwolal sie do relacji prac przez nie i polaczyl wyniki. ad12. set autotrace off; drop index prac_nazwisko_idx; create index prac_nazw_placa_idx on prac(nazwisko, placa); set autotrace on explain; select count(*) from prac where nazwisko like 'Prac1%'; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 INDEX (RANGE SCAN) OF 'PRAC_NAZW _PLACA_IDX' (NON-UNIQUE) Optymalizator skorzystal z indeksu poniewaz warunek w where odnosi sie po pierwszego czlonu indeksu - nazwiska i ma postac z % na koncu. Nie mamy dostepu do tabeli poniewaz nie potrzebujemy konkretnych wartosci, tylko ilosc. Zliczamy poprzez sortowanie aggregate. select count(*) from prac where nazwisko like 'Prac1%' and placa > 100; > 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 INDEX (RANGE SCAN) OF 'PRAC_NAZW _PLACA_IDX' (NON-UNIQUE) Korzystamy z indeksu poniewaz mozemy na podstawie Prac1% wybrac z niego odpowiednia czesc drzewa i dla niej sprawdzic druga czesc indeksu dla placa > 100. Nie mamy dostepu do tabeli bo wsz informacje sa w indesie. select count(*) from prac where placa > 100; > 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (FULL) OF 'PRAC' Mamy tutaj pelen przeglad tabeli prac, poniewaz mamy warunek na placy a nasz indeks skonkatenowany na pierwszej pozycji ma nazwisko i trzebabyloby przejrzec caly indeks dla kazdego nazwiska i sprawdzic czy placa jest > 100 co oznacza i tak przejrzenie wszystkich elemento w tabeli. ad13. set autotrace off; create index prac_plec_idx on prac(plec); set autotrace on explain; select count(*) from prac where plec='M' and id_prac between 100 and 110; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' 3 2 INDEX (RANGE SCAN) OF 'PRAC_PLEC_IDX' (NON-UNIQUE) Optymalizator skorzystal z indeksu na plci, poprzez dostep range scan (poniewaz jest nieunikalny). Dlaczego debil nie wzial pod uwage indeksu na id_prac? Bo preferuje warunki z wyborek konkretnej wartosci a nie przedzialu... chyba. ad14. alter session set optimizer_goal = choose; set autotrace on explain; select count(*) from prac where plec='M' and id_prac between 100 and 110; Nic sie nie zmienilo. Zostal uzyty optymalizator regulowy bo nie ma statystyk. ad15. set autotrace off; select table_name, last_analyzed, num_rows from user_tables where table_name='PRAC'; select column_name, num_distinct, low_value, high_value num_buckets from user_tab_columns where table_name = 'PRAC'; select index_name, last_analyzed, num_rows from user_indexes where table_name='PRAC'; select * from user_tab_histograms where table_name='PRAC'; Brak jakichkolwiek statystyk. ad16. set autotrace off; analyze table prac compute statistics for table; select table_name, last_analyzed, num_rows from user_tables where table_name='PRAC'; select column_name, num_distinct, low_value, high_value num_buckets from user_tab_columns where table_name = 'PRAC'; select index_name, last_analyzed, num_rows from user_indexes where table_name='PRAC'; select * from user_tab_histograms where table_name='PRAC'; Mamy w efekcie statystyki tylko dla pierwszego zapytania select. ad17. set autotrace off; analyze table prac compute statistics for all indexes; select table_name, last_analyzed, num_rows from user_tables where table_name='PRAC'; select column_name, num_distinct, low_value, high_value num_buckets from user_tab_columns where table_name = 'PRAC'; select index_name, last_analyzed, num_rows from user_indexes where table_name='PRAC'; select * from user_tab_histograms where table_name='PRAC'; Mamy teraz wyniki dla tabeli (zapytanie 1) oraz dla indeksow (zapytanie 3). ad18. set autotrace off; analyze table prac compute statistics for all indexed columns; select table_name, last_analyzed, num_rows from user_tables where table_name='PRAC'; select column_name, num_distinct, low_value, high_value num_buckets from user_tab_columns where table_name = 'PRAC'; select index_name, last_analyzed, num_rows from user_indexes where table_name='PRAC'; select * from user_tab_histograms where table_name='PRAC'; Po podaniu for all indexed columns dostajemy wszystkie statystyki, a optymalizator nie zaklada teraz rozkladu wartosci jako rownomiernego. ad19. set autotrace on explain; select count(*) from prac where plec='M' and id_prac between 100 and 110; > 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=4) 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' (Cost=3 Card=8 Bytes=32) 3 2 INDEX (RANGE SCAN) OF 'PRAC_PK' (UNIQUE) (Cost=2 Card=10) Widzimy duza zmiane bo nasz glupi reguloweic nie wiedzial ze trzeba uzyc indeksu prac_pk a kosztowy juz wiedzial jaki jest rozklad wartosci plci i id_prac i bardziej opyla sie wziac 10 rowid z indesku prac_pk i spradzic czy maja plec='M', niz tysiace z indeksu prac_plec_idx i potem sprawdzac id. Dla indexu prac_pk mamy rnage scan poniewaz w where mamy between and. Potem na podstawie uzyskanych rowid czytamy tabele. Sortowanie aggregate dla count(*) ad20. select count(*) from prac; 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1) 1 0 SORT (AGGREGATE) 2 1 INDEX (FAST FULL SCAN) OF 'PRAC_ PK' (UNIQUE) (Cost=4 Card=10000) nie ma dostępu do tabeli. wszystkie informacje s± dostępne w indeksie założonym na kluczu głównym ad21. set autotrace off; create index prac_czy_etat_idx on prac(czy_etat); analyze table prac compute statistics for table; analyze table prac compute statistics for all indexes; analyze table prac compute statistics for all indexed columns; set autotrace on explain; select count(*) from prac where czy_etat='T' and plec='K'; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=1 Bytes=2) 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (FULL) OF 'PRAC' (C ost=6 Card=1800 Bytes=3600) Ozesz fuck, nie uzyl zadnego indeksu? Cos mu odjebalo. Spojrzmy : COLUMN_NAME NUM_DISTINCT LOW_VALUE NUM_BUCKETS PLEC 2 4B 4D CZY_ETAT 2 4E 54 Atrybuty plec i czy_etat maja zaledwie 2 rozne wartosci. INDEX_NAME LAST_ANA NUM_ROWS PRAC_CZY_ETAT_IDX 04/12/09 10000 PRAC_PLEC_IDX 04/12/09 10000 TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE ENDPOINT_ACTUAL_VALUE PRAC PLEC 2000 3,9007E+35 PRAC PLEC 10000 4,0046E+35 PRAC CZY_ETAT 1000 4,0565E+35 PRAC CZY_ETAT 10000 4,3680E+35 Gdyby wybrano z indeksow rowid i je skonkatenowano to by sie to nie opylalo pewnie ze wzgledu na rozklad wartosci, gdyz jak zmienimy zapytanie na : set autotrace on explain; select count(*) from prac where czy_etat='N' and plec='K'; mamy : Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=1 Bytes=2) 1 0 SORT (AGGREGATE) 2 1 BITMAP CONVERSION (COUNT) 3 2 BITMAP AND 4 3 BITM AP CONVERSION (FROM ROWIDS) 5 4 &nbs p; INDEX (RANGE SCAN) OF 'PRAC_CZY_ETAT_IDX' (NON-UNI QUE) (Cost=2) 6 3 BITM AP CONVERSION (FROM ROWIDS) 7 6 &nbs p; INDEX (RANGE SCAN) OF 'PRAC_PLEC_IDX' (NON-UNIQUE) (Cost=4) i juz mu sie opyla skorzystac z indeksow. ad22. drop index prac_plec_idx; drop index prac_czy_etat_idx; analyze table prac delete statistics; analyze table prac compute statistics for table; analyze table prac compute statistics for all indexes; analyze table prac compute statistics for all indexed columns; --> 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card= 1Bytes=6) --> 1 0 SORT (AGGREGATE) --> 2 1 TABLE ACCESS (FULL) OF 'PRAC' (C ost=6 Card=1 Bytes=6) --> zmieniła się liczność, ze względu na brak histogramów dot. usuniętych indeksów ad23. set autotrace off; create bitmap index prac_plec_bmp_idx on prac(plec) compute statistics; create bitmap index prac_czy_etat_bmp_idx on prac(czy_etat) compute statistics; analyze table prac compute statistics for table; analyze table prac compute statistics for all indexes; analyze table prac compute statistics for all indexed columns; ad24. alter session set optimizer_goal = rule; set autotrace on explain; select count(*) from prac where czy_etat='T' and plec='K'; > lan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=RULE 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (FULL) OF 'PRAC' Mamy full scan tabeli.(zadnych indeksow b-drzewo). Wynikaz tego ze indeksy bitmapowe pod uwage bierze tylko optymalizator kosztowy. ad25. alter session set optimizer_goal = choose; set autotrace on explain; select count(*) from prac where czy_etat='T' and plec='K'; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=2) 1 0 SORT (AGGREGATE) 2 1 BITMAP CONVERSION (COUNT) 3 2 BITMAP AND 4 3 BITM AP INDEX (SINGLE VALUE) OF 'PRAC_PLEC_BMP_IDX' 5 3 BITM AP INDEX (SINGLE VALUE) OF 'PRAC_CZY_ETAT_BMP_IDX' Widac zmiany. Optymalizator skorzystal z indexow bitmapowych. Wartosci atrybutow czy_etat oraz plec maja mala selektywnosc, wiec nadaja sie do indeksowania bitmapowego. POniewaz warunki w klauzuli where sa rownosciowe mamy po jednym indeksie bitmapowym dla kazdego z nich (single value). Na tyc hindeksach mozemy latwo wykonywac operacje logiczne np and. reszta to zliczenie jedynek w wynikowej bitmapie. ad26. set autotrace off; create index prac_placa_idx on prac(placa); analyze table prac compute statistics for table; analyze table prac compute statistics for all indexes; analyze table prac compute statistics for all indexed columns; ad27. set autotrace on explain; select nazwisko from prac where placa < 2; > 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=21 Bytes=231) 1 0 INDEX (FAST FULL SCAN) OF 'PRAC_NAZW_PLACA_IDX ' (NON-UNIQUE) (Cost=5 Card=21 Bytes=231) No i wyjebal full scan osiol a autorzy tego cwiczenia sie spuszczali zeby skorzystal z indeksu, wiec wymuszamy jego uzycie: set autotrace on explain; select/*+INDEX(prac prac_placa_idx)*/ nazwisko from prac where placa < 2; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=23 Card=21 Bytes=231 ) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' (Cost= 23 Card=21 Bytes=231) 2 1 INDEX (RANGE SCAN) OF 'PRAC_PLAC A_IDX' (NON-UNIQUE) (Cost=2 Card=21) Ale koszt jest wiekszy, ciekawe dlaczego, a moze dlatego ze index ma kolejnosc leksykograficzna a nie numeryczna, bardzo mozliwe , choc sam nie wiem... bo w 29. juz korzysta. select nazwisko from prac where ROUND(placa) < 2; > 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=500 Bytes=550 0) 1 0 TABLE ACCESS (FULL) OF 'PRAC' (Cost=6 Card=500 Bytes=5500) Nie jest to index oparty na wyrazeniu tylko zwykly, wiec nie zawiera info o zaokraglonej placy. ad28. alter session set query_rewrite_enabled=true; alter session set query_rewrite_integrity=trusted; ad29. set autotrace on explain; create index prac_placa_fun_idx on prac(round(placa)); select nazwisko from prac where ROUND(placa) < 2; > Plan wykonywania ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=500 Bytes=550 0) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PRAC' (Cost= 5 Card=500 Bytes=5500) 2 1 INDEX (RANGE SCAN) OF 'PRAC_PLAC A_FUN_IDX' (NON-UNIQUE) (Cost=2 Card=90) Skubaniec skorzystal z naszego indeksu funkcyjnego, az dziw, widocznie wolal wykorzystac nasze gotowe zaokraglenia w indeksie. range scan poniewaz mamy < 2. ad30. dostępne indeksy PRAC_CZY_ETAT_BMP_IDX PRAC_NAZW_PLACA_IDX PRAC_PK PRAC_PLACA_IDX PRAC_PLEC_BMP_IDX select * from prac order by id_prac; --> INDEX (FULL SCAN) OF 'PRAC_PK' ( UNIQUE) (Cost=21 Card=10000) select * from prac order by id_prac desc; --> INDEX (FULL SCAN DESCENDING) OF 'PRAC_PK' (UNIQUE) (Cost=21 Card=10000) select * from prac order by nazwisko; --> 1 0 SORT (ORDER BY) (Cost=73 Card=10000 Bytes=300000) --> 2 1 TABLE ACCESS (FULL) OF 'PRAC' (Cost=6 Card=10000 Bytes=300000) select distinct nazwisko from prac; --> 1 0 SORT (UNIQUE) (Cost=36 Card=10000 Bytes=80000) --> 2 1 TABLE ACCESS (FULL) OF 'PRAC' (C ost=6 Card=10000 Bytes=80000) select nazwisko from prac group by nazwisko; --> 1 0 SORT (GROUP BY) (Cost=36 Card=10000 Bytes=8000 0) --> 2 1 TABLE ACCESS (FULL) OF 'PRAC' (C ost=6 Card=10000 Bytes=80000) ??? nie ma indeksu na nazwisku ale jest na nazw+placa... ale go nie wziął czemu? we wszystkich przypadkach okazało się, że korzystniej jest wykonać TABLE ACCESS (FULL) niż biegać po indeksach pamiętamy, że li¶cie indeksów tworz± listę dwukierunkow± i st±d bierzemy row id.