我有一个数据库表,其中包含客户详细信息,当他们打电话时我们使用他们的电话查找他们的详细信息,但这通常需要大约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
答案 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),只有在真正需要的时候(我假设在这里并非如此)。