1.通过IO逻辑读、通过查看图示的查询计划、通过其优化后而执行的SQL语句,才是优化SQL的真正途径。
查询优化的一个重要部分是要确定是否在某个索引上执行SEEK操作,如果是这样,就找到了具有最佳性能的索引。大部分情况下,查询引擎能出色地查找到存在的索引。
2.一条正确的老规则是:在结果集相同的情况下,连接比子查询具有更好的性能。
3.当我们比较查询计划中那一个最好时,事实上我们更愿意用SET STATISTICS PROFILE ON而不是SET SHOWPLAN_TEXT ON。这些工具将以百分率的方式明确给你展示每一个查询计划步骤的处理消耗。这可以告诉你每种选择的或多或少的查询消耗情况。你还可以同时运行两个或更多查询来看哪个执行的最好。4.关于连接时哪个表为outer表,哪个为inner表,我发现sql server会自动给你安排,和你写的位置无关,它自动选择数据量小的表为outer表, 数据量大的表为inner表。
5.嵌套循环连接(Nested Loops)适用范围
如果外部输入非常小,而内部输入非常大并且已预先建立索引,那么嵌套循环联接将特别有效率。6.合并联接(Merge)
指两个表在on的过滤条件上都有索引, 都是有序的, 这样, join时, sql server就会使用Merge join, 这样性能更好.如果一个有索引,一个没索引,则会选择Nested Loops join.
7.哈希联接(Hash)
如果两个表在on的过滤条件上都没有索引, 则就会使用Hash join.也就是说, 使用Hash join算法是由于缺少现成的索引.
8.IO统计 SET STATISTICS IO ON执行SET STATISTICS IO ON,再执行,SELECT * FROM Tbl_1
表'Tbl_1'。扫描计数 6,逻辑读取 14533 次,物理读取 0 次,预读 8 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
扫描计数:显示了表被访问的次数。物理读:指从磁盘读取的页数,该值总是小于或等于逻辑读的值。
最简单的说法:逻辑读读内存、物理读读硬盘。
9.统计表大小
exec sp_spaceused Tbl_1, true
name | rows | reserved | data | index_size | unused |
Tbl_1 | 340289 | 290264 KB | 116264 KB | 172592 KB | 1408 KB |
rows显示表的行数。
data显示116264KB, 我们知道8k一个数据页,116264/8=14533,和前面逻辑读取 14533 次数据完全吻合,也就是说,刚才是全表扫描,所有的数据页都读取了一次。
reserved:290264KB,算下来有283M多。
index_size:172592KB, 也有158M多呢,索引也不便宜。
10. 观察索引对数据表的影响
先把索引都删除掉,只统计原始表
name | rows | reserved | data | index_size | unused |
Tbl_1 | 340289 | 116976 KB | 116264 KB | 48 KB | 664 KB |
同样的行数,同样的data,因为少了索引,少占了许多空间。
此时再执行,SELECT * FROM Tbl_1
表 'Tbl_1'。扫描计数 6,逻辑读取 14533 次,物理读取 217 次,预读 4564 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
给Tbl_1的主键建立聚集惟一索引后,再统计大小。
exec sp_spaceused Tbl_1, true
name | rows | reserved | data | index_size | unused |
Tbl_1 | 340290 | 112264 KB | 111736 KB | 392 KB | 136 KB |
此时再执行,SELECT * FROM Tbl_1
表 'AD_GF_Logic_BusinessOpportunity'。扫描计数 1,逻辑读取 14016 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
然后再加一个索引,和初始时一样,此时空间骤然变大了:
name | rows | reserved | data | index_size | unused |
Tbl_1 | 340290 | 208856 KB | 111736 KB | 96864 KB | 256 KB |
但,和初始时290264 KB相比,208856 KB还是少了80多M,其中索引从172592 KB减少到96864 KB, 减少了80M,可见,重建一遍索引,能省80M的空间。
11.索引碎片
SQLServer数据库随着使用时间的增长,会让人觉得越来越慢,这个和你平时没有合理的维护计划有关系,定期处理索引碎片是一个必不可少的工作内容之一。
先统计表大小,exec sp_spaceused Tbl_2, true
name | rows | reserved | data | index_size | unused |
Tbl_2 | 237533 | 89816 KB | 89232 KB | 304 KB | 280 KB |
表 'Tbl_2'。扫描计数 1,逻辑读取 11190 次,物理读取 0 次,预读 62 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
DBCC SHOWCONTIG 正在扫描 'Tbl_2' 表...表: 'Tbl_2' ;索引 ID: 1,数据库 ID: 7- 扫描页数................................: 11154- 扫描区数..............................: 1409- 区切换次数..............................: 9612- 每个区的平均页数........................: 7.9- 扫描密度 [最佳计数:实际计数].......: 14.51% [1395:9613]- 逻辑扫描碎片 ..................: 90.33%- 区扫描碎片 ..................: 75.94%- 每页的平均可用字节数.....................: 2733.6- 平均页密度(满).....................: 66.23%解读:逻辑扫描碎片:无序页的百分比。该百分比应该在0%到10%之间,高了则说明有外部碎片。
此表逻辑扫描碎片高达90.33%,说明真该清理碎片了。
删除该表的主键聚集索引后,重新统计大小:
name | rows | reserved | data | index_size | unused |
Tbl_2 | 237533 | 57160 KB | 56904 KB | 144 KB | 112 KB |
从89816 KB到57160, 减少了30多M, data从89232 KB到56904KB,减少了30多M。
DBCC SHOWCONTIG 正在扫描 'Tbl_2' 表...
表: 'Tbl_2' ;索引 ID: 1,数据库 ID: 7- 扫描页数................................: 7113- 扫描区数..............................: 890- 区切换次数..............................: 889- 每个区的平均页数........................: 8.0- 扫描密度 [最佳计数:实际计数].......: 100.00% [890:890]- 逻辑扫描碎片 ..................: 0.01%- 区扫描碎片 ..................: 20.67%- 每页的平均可用字节数.....................: 173.7- 平均页密度(满).....................: 97.85%现在逻辑扫描碎片从90.33%变为0.01%了,区扫描碎片从75.94%变为20.67%了,简直是天壤之别。最后,可以通过右键重建某索引时看一下sql server给统计的碎片总计,触目精心啊!才运行了5年的库啊!
12.非聚集索引比聚集索引还快?
推荐资料:
理解SQL Server的SQL查询计划
SQL Server的执行计划及SQL查询优化实例分析与总结
SQLServer索引碎片和解决方法(收集)
SQLServer索引碎片和解决方法整理索引碎片,提升SQL Server速度