Oracle, taki duży, taki fajny, taki dopracowany...
Dzisiaj kolejny raz stary klient dzwoni i płacze, że mu się dokumenty wolno drukują, że jak puszczą to muszą czekać po ~2 minuty na każdy wydruk. No to się łączę i paczę co się tam dzieje. Zapytanie wyciągające dane dla jednego wydruku (nie biczujcie za nazwy tabel/pól - ja tam pracowałem tylko jakiś czas, stan zastany, a z byłymi klientami mam na tyle dobry kontakt, że czasami dzwonią do mnie bo mają większą szansę, że pomogę :p):
Kopiuj
SELECT z.nr_zam, z.data_zam, n.id_k,n.id_a, CASE WHEN Trim(a.komunikator) IS NULL THEN a.miasto ELSE a.komunikator END miasto,Trim(a.ulica || ' ' || a.numer) adres,'$' || to_char(z.nr_zam)||to_char(z.data_zam, 'yyyymmdd') kod_zam,Sum(p.ilosc_kg) masa
FROM zhzbiorcze z, zhnag n, zhpoz p, koadresy a
WHERE n.nr_zam = z.nr_zam AND n.data_zam = z.data_zam AND p.nr_zam = z.nr_zam AND p.data_zam = z.data_zam AND a.id_k = n.id_k AND a.id_a = n.id_a AND a.is_deleted = 'N'
GROUP BY z.nr_zam,z.data_zam,n.id_k,n.id_a,a.komunikator,a.miasto,a.ulica,a.numer,z.kolejnosc
ORDER BY z.kolejnosc desc
Jeszcze taka uwaga - oracle długo nie rozpoznawał słowa join - left join zapisywało się tak: SELECT * FROM a, b WHERE b.id(+) = a.id co oznacza SELECT * FROM a LEFT JOIN b on b.id = a.id - ten (+) oznaczał, że z "tej strony" rekordu może nie być. ANSI Join jest wspierany od wersji 9i (której chyba nikt nie używał, więc tak naprawdę od wersji 10g, czyli gdzieś od 2003 roku)
Plan:
Kopiuj
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 114 | 6 (17)| 00:00:01 |
| 1 | SORT GROUP BY | | 1 | 114 | 6 (17)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 114 | 5 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 17 | 114 | 5 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 98 | 3 (0)| 00:00:01 |
| 5 | MERGE JOIN CARTESIAN | | 1 | 77 | 3 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | ZHZBIORCZE | 1 | 35 | 2 (0)| 00:00:01 |
| 7 | BUFFER SORT | | 2821 | 115K| 1 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID BATCHED| KOADRESY | 2821 | 115K| 1 (0)| 00:00:01 |
|* 9 | INDEX SKIP SCAN | IDX_KOADRESY | 1215 | | 1 (0)| 00:00:01 |
|* 10 | TABLE ACCESS BY INDEX ROWID BATCHED | ZHNAG | 1 | 21 | 0 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | IDX_ZHNAG_DATA_KON | 1 | | 0 (0)| 00:00:01 |
|* 12 | INDEX RANGE SCAN | I_ZHPOZ1 | 17 | | 2 (0)| 00:00:01 |
| 13 | TABLE ACCESS BY INDEX ROWID | ZHPOZ | 1 | 16 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------
szybka zmiana na:
Kopiuj
SELECT z.nr_zam, z.data_zam, n.id_k,n.id_a, CASE WHEN Trim(a.komunikator) IS NULL THEN a.miasto ELSE a.komunikator END miasto,Trim(a.ulica || ' ' || a.numer) adres,'$' || to_char(z.nr_zam)||to_char(z.data_zam, 'yyyymmdd') kod_zam,Sum(p.ilosc_kg) masa
FROM zhzbiorcze z, zhnag n, zhpoz p, koadresy a
WHERE n.nr_zam = z.nr_zam AND n.data_zam = z.data_zam AND p.nr_zam(+) = z.nr_zam AND p.data_zam(+) = z.data_zam AND a.id_k = n.id_k AND a.id_a = n.id_a AND a.is_deleted = 'N'
GROUP BY z.nr_zam,z.data_zam,n.id_k,n.id_a,a.komunikator,a.miasto,a.ulica,a.numer,z.kolejnosc
ORDER BY z.kolejnosc desc
czyli tabela zhpoz zamiast łączyć się przez join łączy się przez left join i nagle czas wykonania zapytania spada do ok. 3-4 sekund... A nowy plan wygląda tak:
Kopiuj
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 114 | 6 (17)| 00:00:01 |
| 1 | SORT GROUP BY | | 1 | 114 | 6 (17)| 00:00:01 |
| 2 | NESTED LOOPS OUTER | | 1 | 114 | 5 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 98 | 3 (0)| 00:00:01 |
| 4 | MERGE JOIN CARTESIAN | | 1 | 77 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | ZHZBIORCZE | 1 | 35 | 2 (0)| 00:00:01 |
| 6 | BUFFER SORT | | 2821 | 115K| 1 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID BATCHED| KOADRESY | 2821 | 115K| 1 (0)| 00:00:01 |
|* 8 | INDEX SKIP SCAN | IDX_KOADRESY | 1215 | | 1 (0)| 00:00:01 |
|* 9 | TABLE ACCESS BY INDEX ROWID BATCHED | ZHNAG | 1 | 21 | 0 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | IDX_ZHNAG_DATA_KON | 1 | | 0 (0)| 00:00:01 |
| 11 | TABLE ACCESS BY INDEX ROWID BATCHED | ZHPOZ | 1 | 16 | 2 (0)| 00:00:01 |
|* 12 | INDEX RANGE SCAN | I_ZHPOZ1 | 17 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------
ZHZBIORCZE to tabela z numerami zamówień do drukowania i nigdy tam nie ma więcej niż 10-20 rekordów więc indeks na tej tabeli jest co najmniej dyskusyjny.
W obu przypadkach dane zwracane są dokładnie takie same - zawsze jest przynajmniej jeden rekord w p odpowiadający z.
Jeśli ktoś miał by jakieś wytłumaczenie dlaczego tak to chętnie wysłucham bo dla mnie, mimo iż wiem na czym polega różnica w zapytaniach, to nie jestem w stanie pojąć różnicy w czasie wykonania.