[MSSQL] CASE strasznie wydłuża zapytanie - jak poprawić?

[MSSQL] CASE strasznie wydłuża zapytanie - jak poprawić?
JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0

Cześć, mam takie zapytanie:

Kopiuj
select fi.ID, (case when fi.st = 1 then max(ev2.date) 
	else max(ev.date) end) as lastOut 
from tabela1 fi
left join tabela2 ed on ed.fiID = fi.ID
left join tabela3 ev on ev.eID = fi.eID and
		dbo.get_date(ev.date) = dbo.get_date(ed.endDate) and
		ev.deleted = 0 and (ev.isOut = 1 or ev.isOut is null)
left join tabela3 ev2 on ev2.eID = fi.eID and
		ev2.date <= ed.endDate and
		ev2.deleted = 0 and (ev2.isOut = 1 or ev2.isOut is null)
group by fi.id, fi.scheduleType
order by fi.ID	    

Jak widać joinuję 2 razy jedną tabelę, ale z różnymi warunkami.
W zależności of pola w tabeli FI odpowiednio liczę lastOut.

Niestety to zapytanie zajmuje mi ponad 20 sekund, gdzie bez case'a zajmuje sekundę, może kilka.
Czemu tak się wydłużyło i jak mógłbym to zoptymalizować?

[dopisane]
OK, udało mi się dodać po jednym warunku do złączeń i trwa teraz 3 sekundy. Jednak, jeśli ktoś widzi jeszcze jakąś opcję optymalizacji, to bym prosił o jakieś wskazówki :)

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

Co mowi execution plan?

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0
johny_bravo napisał(a)

Co mowi execution plan?

http://rapidshare.com/files/245900982/juhas.sqlplan.html

Mówi, że sort zajmuje 77%. W prawdzie nie umiem za bardzo czytać planów, ale po usunięciu ORDER BY czas wykonania wcale nie był krótszy.

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

Strasznie duzo wynikow dostajesz w tym zapytaniu - 91 tys., byc moze dlatego sortowanie jest najciezsze. Po drugie case musi sie wykonac dla wszystkich tych wartosci, co tez nie jest bez znaczenia. Co jest najbardziej obciazajaca operacja po usunieciu sorta?

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0

Plan bez sorta wygląda następująco:

http://rapidshare.com/files/247692725/juhas.sqlplan.html

Najwięcej(66%) Hash Match(Aggregate)

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

To ciagle jest ponad 100 tys. wynikow. Czemu taka ilosc jednym selectem? Przeciez i tak nie obrobisz tego po stronie klienta. Ten Hash Match jest wlasnie dla dopasowania case'a czyli fi.scheduleType = 1. Dla takiej ilosci wierszy to dopasowanie nie ma prawa byc szybkie ;)

Sprobuj ograniczyc ilosc wierszy, ktore beda dopasowywane casem.

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0
johny_bravo napisał(a)

Sprobuj ograniczyc ilosc wierszy, ktore beda dopasowywane casem.

Hmmm, no właśnie nie za bardzo mogę, bo potrzebuję je wszystkie. To zapytanie to część większego działania po stronie serwera :)

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

To podziel to zapytanie na 2, jedno z where x = 1 i drugie where x <> 1. Sprawdz jak sie wtedy zachowa. Czy na scheduleType jest index? Jaka jest roznorodnosc wartosci scheduleType?

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0

ScheduleType to liczba z zakresu 0..3
Trudno stwierdzić, jaka jest różnorodność, a na tabeli, w której to pole jest jest clustered_index(jak na każdej innej).

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

W takim wypadku prawdopodobnie i tak nie skorzystasz z indeksu, zbyt mala selektywnosc kolumny. Sprawdzales wariant z where?

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0
johny_bravo napisał(a)

Sprawdzales wariant z where?

Jeszcze nie. Ale nie rozumiem za bardzo, mam po prostu wpisać: "where x = 1", czy za to x podstawić coś konkretnego? ;>

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

Ekhem... x, czyli fi.st = 1... Nie chcialo mi sie tego przepisywac. Skoro to czesc wiekszego zapytania, to fi.st stanowi de facto czesc wiekszego warunku. Sprobuj wiec zapytanie zapisac za pomoca 2 zapytan, gdzie w jednym bedzie fi.st = 1 a w drugim fi.st <> 1. Where powinien byc potraktowany troche lepiej niz case.

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0
johny_bravo napisał(a)

Ekhem... x, czyli fi.st = 1... Nie chcialo mi sie tego przepisywac. Skoro to czesc wiekszego zapytania, to fi.st stanowi de facto czesc wiekszego warunku. Sprobuj wiec zapytanie zapisac za pomoca 2 zapytan, gdzie w jednym bedzie fi.st = 1 a w drugim fi.st <> 1. Where powinien byc potraktowany troche lepiej niz case.

Tak, faktycznie jest dużo lepiej. Teraz mam 1 sekundę zamiast 4 :)
THX!

johny_bravo
  • Rejestracja: dni
  • Ostatnio: dni
0

To mnie cieszy :)

Jak chcesz jeszcze docisnac to podeslij kolejne plany wykonania ;) Moze sie da.

JU
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5052
0
johny_bravo napisał(a)

To mnie cieszy :)

Jak chcesz jeszcze docisnac to podeslij kolejne plany wykonania ;) Moze sie da.

No, dzisiaj przerobiłem jakąś 1/4 bazy i wraz z Twoją wskazówką dostałem czas mniejszy o jakieś 10 - 15 sekund na całej operacji. Wiem, że można jeszcze szybciej ;)

Tak więc kolejna porada by się przydała tu: http://4programmers.net/Forum/viewtopic.php?id=145767 :D

Podejrzewam, że select zawsze będzie szybszy od kursora.
A to sprawdzenie jest wykonywane wiele, wiele razy :)

crowa
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 295
0

dosyc czesto przerabialem ten problem w pracy i okazuje sie ze dla duzej ilosci danych case jest wolniejszy niz where + union all.

Sprobuj moze podzielic case na osobne select'y z uwzglednieniem warunku z case i je union all.

Kopiuj
SELECT a, b , CASE WHEN c = 1 THEN c + 20 ELSE c END
from Tab
where warunek

przerabialem na

Kopiuj
SELCT a, b, c+20
from Tab
WHERE warunek 
            AND c = 1
UNION ALL
SELECT a,b,c
from Tab
WHERE c <> 1

index na c (zwykly nie klastrowy)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.