sobota, 22 listopada 2014

SQL: Zapytanie działa szybko, ale w procedurze nie.... o co chodzi?

Problem wydajności zapytań SQL bardzo często można rozwiązać już na etapie ich samej "wewnętrznej" optymalizacji (bez myślenia o indeksach i innych sztuczkach).

Czasami jednak, podczas optymalizacji przytrafiają się bardzo "dziwne" przypadki...


Procedura SQL = "drogie opakowanie" ?

Niedawno przytrafił mi się interesujący przypadek.Otrzymałem zgłoszenie, iż jedna funkcja systemu działa koszmarnie wolno.

Okazało się, że istotnie, po wychwyceniu odpowiedniego wywołania procedury w SQL Server Profiler ta wykonywała się przez dobre 10 sekund, zanim zwróciła wyniki, w chwili kiedy powinna zajmować nie więcej niż "chwilę" (poniżej sekundy).

dla uproszenia:
alter procedure abc
@a int,
@b int,
@c nvarchar(100)
as
begin
  select
     *
  from table_a ta
  join table_b tb on  ta.id=tb.ref_ta
  where 
  (
    ta.a=@a
    and tb.b = @b 
  )
    or tb.c=@c

end


Pierwszym krokiem, jaki wykonuję w celu optymalizacji jest "wyjęcie" ciała procedury, wrzucenie do nowego zapytania łącznie z parametrami i szukanie krok po kroku, gdzie jest "hamulec" czyli

alter procedure abc
@a int,
@b int,
@c nvarchar(100)
as
begin
...
end

zastąpione


declare @a int=10
declare @b int = 20
declare @c nvarchar(100)='asdf'


I tutaj niespodzianka:po "wyjęciu" procedury do zapytania śmiga aż miło! Pierwsza myśl (po wyjściu z szoku): bardzo drogie "opakowanie" :) A przecież nie wypada wywoływać samego "zapytania" z poziomu aplikacji bo... nie. I tyle.

Co robić? Jak żyć?


Na szczęście (pomijając już fakt tegoż dziwnego przypadku) jest na to lekarstwo:
Stworzenie zmiennych "lokalnych" (wewnątrz procedury), które przyjmą wartości PARAMETRÓW, następnie wewnątrz wykorzystywane będą nasze nowe zmienne :)

 czyli:

alter procedure abc
@a int,
@b int,
@c nvarchar(100)
as
begin
  declare @la int=@a
  declare @lb int=@b
  declare @lc nvarchar(100) = @c
  select
     *
  from table_a ta
  join table_b tb on  ta.id=tb.ref_ta
  where 
  (
    ta.a=@la
    and tb.b = @lb 
  )
    or tb.c=@lc

end

VOILA :)