2017-03-22 108 views
6

Mam tabelę bazy danych, która zawiera dane klienta, a kiedy dzwonią, używamy telefonu do sprawdzenia ich danych, ale zwykle zajmuje to około 2-3 sekundy, ale ostatnio trwało to 5 sekund bez dodatkowych danych. Jeśli wyślę zapytanie do tabeli używając parametru home_phone_no = '441903354676', funkcja ta powróci w sekundach. Ale jeśli jest to zapytanie z użyciem home_phone_no = '441903354676' lub business_phone_no = '441903354676', to zajmuje to 5 sekund.Moje zapytanie SQL trwa zbyt długo, gdy używa się indeksu nieklastrowego.

Teraz istnieje około 1,4 mln rekordów klientów. Ale jeśli ktokolwiek może zobaczyć cokolwiek oczywistego lub dostarczyć kilka pomocnych sugestii, jest to bardzo mile widziane.

to struktura tabeli

CREATE TABLE [dbo].[CCDB_ICR] 
(
       [bill_account_no] [varchar](10) NOT NULL, 
       [reference_id] [varchar](11) NULL, 
       [bill_account_status] [varchar](2) NULL, 
       [customer_type] [varchar](1) NULL, 
       [customer_name] [varchar](56) NULL, 
       [property_address] [varchar](69) NULL, 
       [outer_post_code] [varchar](4) NULL, 
       [inner_post_code] [varchar](3) NULL, 
       [customer_move_in_date] [datetime2](7) NULL, 
       [customer_move_out_date] [datetime2](7) NULL, 
       [debt_flag] [varchar](1) NULL, 
       [payment_category_flag] [varchar](2) NULL, 
       [payment_plan_flag] [varchar](1) NULL, 
       [key_customer_flag] [varchar](1) NULL, 
       [home_phone_no] [varchar](12) NULL, 
       [business_phone_no] [varchar](12) NULL, 
       [contact_type] [varchar](4) NULL, 
       [contact_code] [varchar](1) NULL, 
       [contact_method] [varchar](1) NULL, 
       [contact_date] [date] NULL, 
       [contact_time] [varchar](5) NULL, 
       [contact_status] [varchar](1) NULL, 
       [contact_unit_resp] [varchar](4) NULL, 
       [outstanding_balance] [decimal](9, 2) NULL, 
       [date_time_inserted] [datetime] NOT NULL, 
       [date_time_updated] [datetime] NULL, 

    CONSTRAINT [PK_CCDB_ICR] 
     PRIMARY KEY CLUSTERED([bill_account_no] ASC) 
        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

to struktura index:

CREATE NONCLUSTERED INDEX [NC_Business&Home_Phone$CCDB_ICR] 
    ON [dbo].[CCDB_ICR] ([home_phone_no] ASC, [business_phone_no] ASC) 
    INCLUDE ([bill_account_no]) 

jest tu procedury przechowywanej

ALTER procedure [dbo].[GET_ACCOUNT_BY_PHONE_NUMBER] 
(
@CLI varchar(12), 
@RETURN_VALUE int out, 
@NUMBER_OF_ACCOUNTS int out, 
@ACCOUNT_NUMBER varchar(10) out 
) 
as 
Begin Try 
     declare @ret int 
     Select @ret=COUNT(*) from CCDB_ICR where [email protected] or [email protected] 
     if @ret=0 
     select @RETURN_VALUE=0, 
     @NUMBER_OF_ACCOUNTS=0, 
     @ACCOUNT_NUMBER=null 
     else if @ret=1 
     select @RETURN_VALUE=0, 
     @NUMBER_OF_ACCOUNTS=1, 
     @ACCOUNT_NUMBER=(Select bill_account_no from CCDB_ICR where [email protected] or [email protected]) 
     else if @ret>1 
     select @RETURN_VALUE=0, 
     @[email protected] , 
     @ACCOUNT_NUMBER=null 
end Try 

Begin Catch 
     select @RETURN_VALUE=-1, 
     @NUMBER_OF_ACCOUNTS=null , 
     @ACCOUNT_NUMBER=null 
End Catch 
+0

proszę podać użyte zapytanie, a także plan wykonania w obu przypadkach – TheGameiswar

Odpowiedz

6

Indeks nie może być użyte dla home_phone_no = '441903354676' or business_phone_no = '441903354676', ale może być używane dla home_phone_no = '441903354676' and business_phone_no = '441903354676'.

Nie będzie w stanie użyć drugiej kolumny klucza indeksu bez warunku dla pierwszej kolumny klucza indeksu.

Aby użyć or, należy użyć oddzielnych indeksów wspierających, np:

create nonclustered index [NC_Business&Home_Phone$CCDB_ICR] 
    on [dbo].[CCDB_ICR] ([home_phone_no] asc); 

create nonclustered index [NC_Business&Business_Phone$CCDB_ICR] 
    on [dbo].[CCDB_ICR] ([business_phone_no] asc); 

Ponadto, nie muszą zawierać [bill_account_no] jako dołączonego kolumnie indeksów, ponieważ jest to klucz klastrów, jak i takie jest już domyślnie włączone.

można uprościć całą procedurę aż do:

alter procedure [dbo].[get_account_by_phone_number] (
    @cli varchar(12) 
    , @return_value int out 
    , @number_of_accounts int out 
    , @account_number varchar(10) out 
) as 
begin; 
    set nocount, xact_abort on; 

    set @return_value = 0; 
    set @number_of_accounts = 0; 

    select 
     @number_of_accounts = count(*) 
    , @account_number = case when count(*)=1 then max(bill_account_no) else null end 
    from ccdb_icr 
    where [email protected] 
    or [email protected]; 
end; 
go 

Jeśli nadal występują problemy z wydajnością po utworzeniu odpowiednich indeksów i aktualizowania procedury, należy spróbować zidentyfikować jeśli parametr wąchania jest przyczyną problemu .

chciałbym zacząć this article by Paul White który obejmuje:

SQL Server oferuje szereg podpowiedzi zapytań i innych opcji, aby dostroić zachowanie parametru wąchania:

  • Optymalizuj dla (@ parametr = wartość) zapytanie podpowiedź buduje plan wielokrotnego użytku na podstawie określonej wartości
  • OPTYMALIZACJA DLA (parametr @ NIEZNANY) wykorzystuje średnie statystyki rozkładu dla określonego parametru
  • OPTIMIZE nieznanych używa rozkładu średniej dla wszystkich parametrów (taki sam efekt jak flagą śledzenia 4136)
  • WITH RECOMPILE opcja procedura przechowywana kompiluje świeże plan postępowania dla każdej realizacji
  • opcji (RECOMPILE) wskazówkę kwerendy kompiluje świeżego planu do jednostkowego sprawozdania
    ~ Parameter Sniffing, Embedding, and the RECOMPILE Options - Paul White
+0

Dzięki, więc bieżący indeks jest bezużyteczny i aby zapytanie działało lepiej, pozostałe dwa indeksy muszą zostać zbudowane? – Rusty

+0

To jest zupełnie inna propozycja teraz, gdy wskazałeś, że używasz procedury przechowywanej. SP może cierpieć na sniffing parametrów. Jest wiele kroków do optymalizacji zarówno procedur przechowywanych, jak i indeksów, ale musisz zacząć od przechwytywania planu zapytania. –

0

Nawet poprawnych indeksów (w tym przypadku dwa oddzielne indeksy single-col, jeden na home_phone_no i inne na business_phone_no), gdy ty lub predykaty najprawdopodobniej skończyć ze skanowaniem.

Jest o wiele lepiej używać UNION ALL, być może z CTE aby uniknąć powielania cały zapytanie:

;with x as (
    -- your query here 
) 
select * from x where home_phone_no = @home_phone_no 
union all 
select * from x where business_phone_no = @business_phone_no 

także unikać WITH rekompilacji, to zbyt duży młotek. Zamiast tego użyj opcji (RECOMPILE) i tylko jeśli jest to naprawdę potrzebne (co, jak zakładam, nie ma tu miejsca).

+0

Dzięki temu jest to pomocne – Rusty