SQLServer 索引总结

测试案例:

SET STATISTICS IO ON
SET STATISTICS TIME ON
SET STATISTICS PROFILE ON
SELECT  count(A.CarrierTrackingNumber) FROM SALES.SALESORDERDETAIL A
WHERE A.SalesOrderDetailID>10000 AND  A.SalesOrderDetailID<10100

执行计划:

image

测试loopup 和索引查找:

第一步 走了扫描 和通过聚集索引查找 OutputList字段;

我们这里可以添加索引 不过建议创建包含索引;复合索引快 但是维护起来要复杂些,也影响性能;

我做了下比较:

CREATE NONCLUSTERED INDEX [IX_TEST] ON [Sales].[SalesOrderDetail]
(
    [SalesOrderDetailID] ASC
)
INCLUDE (     [CarrierTrackingNumber]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO


CREATE NONCLUSTERED INDEX [IX_TEST1] ON [Sales].[SalesOrderDetail]
(
    [SalesOrderDetailID],[CarrierTrackingNumber] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

蓝色是包含索引,红色是复合索引,我们无论单独创建哪个索引他都会把第一步合成一个步骤索引查找;(就不截图了)

然后我们做下两者的速度;

DBCC FREEPROCCACHE

image

测试包含、联合索引:

查询优化器智能走向是test1 复合索引;

我们看下io开销:

包含索引test:

image

复合索引:

image

复合索引逻辑读是3,这个很好解释,因为联合索引的索引叶要比包含索引叶子大,包含索引是只是存储包含字段的一个指针,而复合是实际的值;

如果数据量很大,性能差距也很明显:

我们把条件改下,返回的结果集大点,然后下面一句我们使用强制索引,强制语句选择使用包含索引:

image

轻而易举我们发现ix_test1 联合索引 明显绝对性优势;

计算函数对索引影响:

SELECT  count(A.CarrierTrackingNumber) FROM SALES.SALESORDERDETAIL A
WHERE CONVERT(NUMERIC(9,3),A.SALESORDERDETAILID/100)=100

image

这个字段我们上面两个索引都加过了,结果错误的走向了[IX_SalesOrderDetail_ProductID]这个索引;

一般函数在’=’’<’’>’左侧不会走索引! 这个要修改语句逻辑了!

 

SCAN未必是很差的选择:

PKID 列上有个非聚集索引;

pkid=100093 有2万行数据;

pkid=10094 大概有10条结果;

image

下图得到结果是pkid=100093走了聚集索引扫描,优化器认为这样比通过索引叶查询+聚集rid look 要快;

我们可以尝试下 很简单的测试:

SELECT ANAME FROM TEST with(index=ix_pkid) WHERE PKID=100093
SELECT ANAME FROM TEST  WHERE PKID=100093

image

很显然 全表扫描要快;

我再补上IO次数;

image

差距是不是很明显,索引优化器还是比较很聪明;

还一点 我也顺便测试了

我把这个语句打包成存储过程,直接缓存到缓存中;我们看下效果:

CREATE PROC SCAN (@I INT)
AS
SELECT ANAME FROM TEST  WHERE PKID=@I


DBCC FREEPROCCACHE

我们先执行这个数量少的给优化器看,骗她存入缓存计划;

exec scan 100094

image

当我们执行这个一般大概2次,sqlserer就会存入缓存当中;

开始测试第二个:

exec scan 100093

image

 

image

你看很好骗吧,太坑了!!!

 

我们重新编译下:

sp_recompile 'scan'

exec scan 100093

哪天优化器能识别优化这个就更厉害了!!

image

当然我们也有解决办法:

添加包含索引如下:

USE [AdventureWorks2014]
GO
CREATE NONCLUSTERED INDEX IDX_PKIDANAME ON [dbo].[TEST] ([PKID])
INCLUDE ([ANAME])
GO

这样结果就全是seek 了;具体问题具体分析了!

 

一般filter记录越早越好,对筛选条件SARG(SearchArgument)条件写法注意点:

走索引的SARG:

“列运算符<常量或变量>”

“<常量或变量>列运算符”

“=”

“<”

“>“

“>=”

“<=“

“IN”

”BETWEEN“

“LIKE ‘ABC%’”

”AND“

'50000'="SALESORDERID"

不走索引SARG:

NOT

<>

NOT EXISTS

NOT IN

NOT LIKE

CONVERT,UPPER

转载于:https://www.cnblogs.com/kingwwz/p/5960144.html

--计算当前月的实际天数 Create FUNCTION dbo.CalcDaysOfMonth (@time varchar(6)) RETURNS int AS BEGIN DECLARE @Days int DECLARE @Month int DECLARE @Year int SET @Year=SUBSTRING(@time,1,4) SET @Month=SUBSTRING(@time,5,6) if( @Month='1' OR @Month='3' OR @Month='5' OR @Month='7' OR @Month='8' OR @Month='10' OR @Month='12' ) set @Days=31 else if( @Month='4' OR @Month='6' OR @Month='9' OR @Month='11' ) set @Days=30; else if(@Year%400=0 OR (@Year%4=0 AND @Year%1000)) set @Days=29 else set @Days=28 RETURN(@Days) END 学习与做项目中总结的几个实用的sqlserver 函数 希望对大家有帮助 --确定某年某月有多少天 Create FUNCTION DaysInMonth ( @date datetime ) Returns int AS BEGIN RETURN Day(dateadd(mi,-3,DATEADD(m, DATEDIFF(m,0,@date)+1,0))) END --哪一天是输入时间的星期一 Create FUNCTION MondayInDate ( @date datetime ) RETURNS DATETIME AS BEGIN RETURN DATEADD(week, DATEDIFF(week,0,@date),0) END --输入时间的季度的第一天 Create FUNCTION QuarterInDate ( @date datetime ) RETURNS DATETIME AS BEGIN RETURN DATEADD(quarter, DATEDIFF(quarter,0,@date), 0) END --输入时间的季度的天数 Create FUNCTION QuarterDaysInDate ( @date datetime ) RETURNS INT AS BEGIN declare @m tinyint,@time SMALLDATETIME select @m=month(@date) select @m=case when @m between 1 and 3 then 1 when @m between 4 and 6 then 4 when @m between 7 and 9 then 7 else 10 end select @time=datename(year,@date)+'-'+convert(varchar(10),@m)+'-01' return datediff(day,@time,dateadd(mm,3,@time)) END --按指定符号分割字符串,返回分割后的元素个数,方法很简单,就是看字符串中存在多少个分隔符号,然后再加一,就是要求的结果。 Create function Get_StrArrayLength ( @str varchar(1024), --要分割的字符串 @split varchar(10) --分隔符号 ) returns int as begin declare @location int declare @start int declare @length int set @str=ltrim(rtrim(@str)) set @location=charindex(@split,@str) set @length=1 while @location0 begin set @start=@location+1 set @location=charindex(@split,@str,@start) set @length=@length+1 end return @length END --按指定符号分割字符串,返回分割后指定索引的第几个元素,象数组一样方便 Create function Get_StrArrayStrOfIndex ( @str varchar(1024), --要分割的字符串 @split varchar(10), --分隔符号 @index int --取第几个元素 ) returns varchar(1024) as begin declare @location int declare @start int declare @next int declare @seed int set @str=ltrim(rtrim(@str)) set @start=1 set @next=1 set @seed=len(@split) set @location=charindex(@split,@str) while @location0 and @index>@next begin set @start=@location+@seed set @location=charindex(@split,@str,@start) set @next=@next+1 end if @location =0 select @location =len(@str)+1 --这儿存在两种情况:1、字符串不存在分隔符号 2、字符串中存在分隔符号,跳出while循环后,@location为0,那默认为字符串后边有一个分隔符号。 return substring(@str,@start,@location-@start) END select dbo.Get_StrArrayStrOfIndex('8,9,4','',4) --结合上边两个函数,象数组一样遍历字符串中的元素 create function f_splitstr(@SourceSql varchar(8000),@StrSeprate varchar(100)) returns @temp table(F1 varchar(100)) as begin declare @ch as varchar(100) set @SourceSql=@SourceSql+@StrSeprate while(@SourceSql'') begin set @ch=left(@SourceSql,charindex(',',@SourceSql,1)-1) insert @temp values(@ch) set @SourceSql=stuff(@SourceSql,1,charindex(',',@SourceSql,1),'') end return end select * from dbo.f_splitstr('1,2,3,4',',')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值