使用非聚集索引时,我的SQL查询花费的时间太长

时间:2017-03-22 17:25:26

标签: sql sql-server indexing

我有一个数据库表,其中包含客户详细信息,当他们打电话时我们使用他们的电话查找他们的详细信息,但这通常需要大约2-3秒,但最近它花费了5秒而没有额外的数据。如果我使用home_phone_no =' 441903354676'来查询表格。这会在亚秒内返回。但如果使用home_phone_no =' 441903354676'或business_phone_no =' 441903354676'那需要5秒钟。

现在有大约140万的客户记录。但是,如果有人能看到任何明显的东西或提供一些有用的建议,那么最受欢迎。

这是表的结构

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]

这是索引的结构:

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]) 

这是存储过程

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 home_phone_no=@CLI or business_phone_no=@CLI
      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 home_phone_no=@CLI or business_phone_no=@CLI)
      else if @ret>1
      select @RETURN_VALUE=0,
      @NUMBER_OF_ACCOUNTS=@ret ,
      @ACCOUNT_NUMBER=null
end Try

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

2 个答案:

答案 0 :(得分:6)

索引不能用于home_phone_no = '441903354676' or business_phone_no = '441903354676',但可以用于home_phone_no = '441903354676' and business_phone_no = '441903354676'

如果索引键的第一列没有条件,它将无法使用索引键的第二列。

要使用or,您可以使用单独的支持索引,例如:

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); 

此外,您不需要在索引中包含[bill_account_no]作为包含列,因为它是群集密钥,因此已经隐式包含。

您可以将整个过程简化为:

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 home_phone_no=@cli 
     or business_phone_no=@cli;
end;
go

如果在创建适当的索引并更新过程后仍遇到性能问题,则应尝试确定参数嗅探是否导致问题。

我将从this article by Paul White开始,其中包含以下内容:

  

SQL Server提供了一系列查询提示和其他选项来调整参数嗅探的行为:

     
      
  • OPTIMIZE FOR(@parameter = value)查询提示根据特定值构建可重用计划
  •   
  • OPTIMIZE FOR(@parameter UNKNOWN)使用特定参数的平均分布统计信息
  •   
  • OPTIMIZE FOR UNKNOWN使用所有参数的平均分布(与跟踪标志4136相同的效果)
  •   
  • WITH RECOMPILE存储过程选项为每次执行编译新的过程计划
  •   
  • OPTION(RECOMPILE)查询提示为单个语句编译新计划   
    Parameter Sniffing, Embedding, and the RECOMPILE Options - Paul White
  •   

答案 1 :(得分:0)

即使有正确的索引(在这种情况下,两个单独的单列索引,一个在home_phone_no上,另一个在business_phone_no上),当你或者谓词时,你很可能最终会进行扫描。

最好使用UNION ALL,可能使用CTE来避免重复整个查询:

;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

另外,避免使用WITH RECOMPILE,它太大了。改为使用OPTION(RECOMPILE),只有在真正需要的时候(我假设在这里并非如此)。