原文连接:http://www.sqlservercentral.com/articles/Stairway+Series/72286/sql
By David Durant, 2017/10/18 (first published: 2014/11/26)数据库
经过大卫·杜兰特,2017/10/18(第一次出版:2014/11/26)服务器
本文是楼梯系列的一部分:SQL Server的阶梯索引数据库设计
索引数据库设计的基础,告诉开发人员使用数据库设计者的意图。 不幸的是索引时每每是后加上的性能问题出现。 终于在这里是一个简单的系列文章,应该让任何数据库专业迅速“加速”sqlserver
SQL Server的一级索引楼梯介绍了SQL Server索引,通常来讲,和非汇集索引。 做为咱们的第一个案例研究中,咱们演示了索引的潜在好处,当从一个表中检索单个行。 在这个层面上,咱们继续咱们的调查的非汇集索引; 检查他们的贡献好的查询性能的状况下,超越从一个表中检索单个行。性能
将在大多数咱们的水平,咱们引入少许的理论,研究一些指数内部为了帮助解释这个理论,而后执行一些查询。 有或没有执行这些查询索引和性能报告统计数据,这样咱们能够查看指标的影响。测试
咱们将使用从AdventureWorks数据库表的子集,咱们用于一级、集中联系表在这个水平。 咱们将使用一个指数FullName咱们用于一级指数,来讲明咱们的观点。 以确保咱们控制上的索引联系表,咱们会让两个表的副本dbo模式,只有构建FullName指数其中之一。 这将给咱们的控制环境:两份表:一个与一个单一的非汇集索引,一个没有任何索引。spa
注意:
全部TSQL代码所示这楼梯水平能够在文章底部的下载。设计
清单1中的代码的副本Person.Contact表,咱们能够随时从新运行这个批处理咱们但愿从一个“白纸”开始。指针
若是存在(选择*从sys.tables&# 160;在哪里OBJECT_ID=OBJECT_ID(“dbo.Contacts_index”))删除表dbo.Contacts_index;去若是存在(选择*从sys.tables&# 160;在哪里OBJECT_ID=OBJECT_ID(“dbo.Contacts_noindex”))删除表dbo.Contacts_noindex;去选择*成dbo.Contacts_index从Person.Contact;选择*成dbo.Contacts_noindex从Person.Contact;
清单2.1:复制人。 联系表
的一个片断联系人表所示:
ContactID FirstName MiddleName LastName EmailAddress
。
。
1288年劳拉·F·诺曼laura1@adventure-works.com
651年迈克尔彭定康michael20@adventure-works.com
1652伊莎贝拉R詹姆斯isabella6@adventure-works.com
1015年大卫·R·坎贝尔david8@adventure-works.com
1379年Balagane Swaminath balaganesan0@adventure-works.c
742年史蒂夫•施密特steve3@adventure-works.com
1743年香农C郭shannon16@adventure-works.com
1106年约翰·Y陈john2@adventure-works.com
1470年布莱恩Dockter blaine1@adventure-works.com
833年克拉伦斯·R。 Tatman clarence0@adventure-works.com
1834希瑟·吴heather6@adventure-works.com
丹尼斯·H·史密斯1197 denise0@adventure-works.com
560年詹妮弗·J。 Maxham jennifer1@adventure-works.com
1561年被罩Ben-Sacha ido1@adventure-works.com
924年贝基R。 水域becky0@adventure-works.com
。
下面的语句建立咱们的Contacts_index FullName非汇集索引表。
建立索引FullName在Contacts_index(姓,FirstName);
清单2.2 -建立一个非汇集索引
记住一个非汇集索引存储索引键,以及一个书签用来访问表中的实际数据自己。 你能想到的书签做为一种指针。 将来的水平将描述书签,其形式和使用,详细。
的一个片断FullName指数显示,组成的姓和FirstName做为键列,加上书签:
——搜索键列:书签
Russell Zachary =>
Ruth Andy =>
Ruth Andy =>
Ryan David =>
Ryan Justin =>
Sabella Deanna =>
Sackstede Lane =>
Sackstede Lane =>
Saddow Peter =>
Sai Cindy =>
Sai Kaitlin =>
Sai Manuel =>
Salah Tamer =>
Salanki Ajay =>
Salavaria Sharon => 每一个条目包含索引键列和收藏价值。 此外,SQL Server非汇集索引条目有internal-use-only头信息和可能包含一些可选的数据值。 这些将在之后的水平; 也不是重要的在这个时候非汇集索引的基本理解。
如今,咱们须要知道的是,键值使SQL Server可以找到合适的索引条目(年代); 和条目的收藏价值使得SQL Server访问相应的数据表中的行。
索引条目排序的索引键值(s),那么SQL Server能够快速遍历顺序条目。 的扫描序列条目能够开始从一开始的指数,指数,指数中或从任何条目。
所以,若是一个请求要求全部联系人的姓开头字母“S”(LastName像' S % '),SQL Server能够快速导航到第一个“S”条目(“Sabella,迪安娜”),而后遍历索引,使用书签访问的行,直到到达第一个“T”条目; 这时它知道它检索全部的“S”条目。
上述请求执行更快若是全部选中的列索引。 所以,若是咱们发布:
选择FirstName,姓从联系在哪里姓就像' S %;
SQL Server能够快速导航到第一个“S”条目,而后遍历索引条目,忽略了书签和检索数据值直接从索引条目,直到它到达第一个“T”条目。 在关系数据库术语中,该指数“覆盖”查询。
任何SQL操做符的好处从测序数据能够从索引中受益。 这包括ORDER BY、组,不一样的联盟(联盟),并加入…。
例如,若是请求要求计数的联系人的姓,SQL Server能够在第一项开始计数,并进行指数。 每次姓的价值变化,SQL Server输出当前计数,并开始一个新的计数。 与前面的请求,这是一个覆盖查询; SQL Server访问索引,彻底忽略了表。
注意从左到右的顺序的重要性的关键列。 咱们的指数是很是有用的,若是一个请求要求每一个人的姓“艾什顿”,但不多或根本没有帮助若是请求是对每一个人都是谁的名字“艾什顿”。
若是你想执行测试查询,确保你运行该脚本建立两个版本新联系表,dbo.Contacts_index和dbo.Contacts_noindex;建立并运行脚本名,姓指数dbo.Contacts_index。
验证断言在前面的小节中,咱们将在相同的性能统计数据,咱们使用1级和运行一些查询; 有和没有索引。
设置数据io在
设置数据时间在
由于联系人表的AdventureWorks数据库中只有19972行,它将很可贵到有意义的统计值。 咱们大多数的查询会显示CPU时间值为0,因此咱们不显示统计数据的输出时间; 只从统计数字IO,反映了可能的页面数量必须阅读。 这些值将使咱们可以比较查询在相对意义上,肯定哪些查询与索引比别人表现得更好。 若是你想要一个更大的表更现实的计时测试,脚本构建版本的一百万行联系本文表是可用的。 全部的讨论将假设您使用的是标准的19972 -行表。
咱们的第一个查询将被索引的查询; 一个检索一组有限的列的全部联系人的姓始于“S”。 表2.1中给出了查询执行信息。
SQL |
选择FirstName、LastName |
没有索引 |
(2130行受影响) |
与指数 |
(2130行受影响) |
指数的影响 |
从568年读14读IO减小。 |
评论 |
覆盖索引查询是一件好事。 没有索引,找到扫描整个表行。 |
表2.1:覆盖查询运行时执行结果
接下来,咱们修改咱们的查询请求与以前相同的行,但不包括列索引。 表2.2中给出了查询执行信息。
SQL |
SELECT * |
没有索引 |
与以前的查询相同。 (由于它是一个表扫描)。 |
与指数 |
(2130行受影响) |
指数的影响 |
根本没有影响。 |
评论 |
该指数是查询的执行期间从未使用过! |
表2.2:non-covered查询运行时执行结果
这一次,咱们让咱们的查询更多的选择性; 也就是说,咱们缩小被请求的行数。 这增长的几率指数将有利于查询。 表2.3中给出了查询执行信息。
SQL |
SELECT * |
没有索引 |
与以前的查询相同。 (由于它是一个表扫描)。 |
与指数 |
(107行受影响) |
指数的影响 |
IO减小从568年读111读. . |
评论 |
SQL Server 107年访问“Ste %”条目,都是坐落在指数连续。 每一个条目的书签被用来检索到相应的行。 行不位于连续在桌子上。 这个查询索引受益; 但不是它受益第一个查询,查询“覆盖”; 尤为是所须要的IOs数检索每一行。 你可能会认为,阅读107索引条目须要107 + 107 + 107行。 为何只有111读取被要求将覆盖在一个更高的水平。 如今,咱们不多会说,读的是用于访问索引条目; 大部分被用来访问的行。 由于前面的查询请求的2130行,没有受益于指数; 这个查询,要求107行,受益于指数——您可能还想“临界点又在何方? “SQL Server的决定背后的计算也将在将来的水平。 |
表2.3:更多的选择性non-covered查询运行时执行结果
咱们最后将一个聚合查询样例查询; 是一个查询,涉及计算,总计平均等等。 在这种状况下,它是一个查询,告诉咱们名字的范围内重复联系表。
结果,在某种程度上,是这样的:
钢美林1
斯蒂尔琼1
斯蒂尔劳拉2
斯蒂尔曼Shanay 1
Steen海蒂2
蒂芬妮斯特凡诺1
施泰纳Alan 1
查询执行信息表2.4中能够看到。
SQL |
选择名,姓,COUNT(*)“联系人” |
没有索引 |
与以前的查询相同。 (由于它是一个表扫描)。 |
与指数 |
(104行受影响) |
指数的影响 |
从568年读到4读IO减小。 |
评论 |
所需的全部信息的查询索引; 是在理想的指数序列计算数量。 全部的“姓始于Ste”条目中连续指数; 和组内,一个姓/名值的全部条目被组合在一块儿。 不须要访问的表; 也不是任何排序所需的中间结果。 再次,索引,查询是一件好事。 |
表2.4:当运行覆盖聚合查询执行结果
若是咱们改变查询包括列索引,咱们获得了性能结果见表2.5。
SQL |
选择LastName、FirstName MiddleName,COUNT(*)“联系人” |
没有索引 |
与以前的查询相同。 (由于它是一个表扫描)。 |
与指数 |
(105行受影响) |
指数的影响 |
IO减小从568年读111读; 之前同样non-covered查询 |
评论 |
中间工做处理查询时并不老是出如今统计数据。 技术,使用内存或tempdb排序和合并数据的例子。 在现实中,索引的好处可能大于所示的统计信息。 |
表2.5:noncovered聚合查询运行时执行结果
咱们如今知道非汇集索引具备如下特性。 非汇集索引:
和咱们看到的例子中,SQL Server从索引就能够知足要求; 和它彻底忽略了指数; 还有一些经常使用的索引和表的组合。 出于这个缘由,咱们关闭二级经过更新语句在一级的开始。
当一个请求到达您的数据库,SQL Server只有三个可能的方法来访问数据要求的声明:
通常来讲,第一个是理想; 和第二个比第三个。 在即将到来的水平,咱们显示如何增长您的索引的几率将覆盖你的热门查询,以及如何肯定你non-covered足够选择性受益于您的索引查询。 但这将须要更多的详细信息索引的内部结构比咱们尚未。
才能达到这一点,咱们须要引入其余类型的SQL Server指数; 汇集索引。 这是三级的主题。
二级- NonClustered.sql|Level2_MillionRowContactTable.sql
这篇文章的一部分SQL Server的阶梯索引楼梯