在表上创建主键的查询速度快还是创建索引的速度快?

时间:2021-06-27 14:52:37
我有一张表有几百W条记录,其中一列是序号,我们查询的时候是输入序号,我现在想知道的是,把这列序号是创建主键还是把这列创建为这个表其中一个索引的速度快呢(在查询操作时)

谢谢!

110 个解决方案

#1


索引啊

#2


SQLSERVER的主键缺省是聚族索引,效率是最高的,但普通的查询感觉不出差别。而且我认为如果序号这样数据唯一的列用聚族索引有些浪费,因为一个表只能有一个聚族索引,要好好利用。

#3


索引

#4


主键默认索引其实是聚集索引,其适合于返回大批连续记录。它的执行机制其实就是变相的表扫描。如果根据序号查询某条具体记录,还是非聚集索引要快!因为不用检索数据页,可以减少I/O。最好看看你的执行计划。

#5


你在这个序号列上创建主键时,sql server会自动把这个序号列加建一个聚族索引.
聚族索引就像书的目录一样.我认在序号列创建聚族索引意思不好.因为你每一个行就是书的目录了.那这样的目录有什么用了.
一个表只能有一个聚族索引,要好好利用

#6


ashzs对聚族索引的理解可能不是很恰当吧,聚族索引是最快的,因为他在叶节点直接就存储的是数据,而索引存储的是数据存放的位置,等于要多一个步骤才能获得数据。

#7


回:tangchao515(梦回唐朝) 

既然是最快的,为什么把唯一列做为主键(聚集索引)会浪费呢?

#8


to:wangkenping(找有感觉的妹妹) 
您对聚集索引的理解有些问题。
非聚集索引才是像目录一样。聚集索引的叶子节点是存放在数据页中的,其实就是和表数据放在了一起。所以说扫描聚集索引和表扫描(注意这里没有说全表扫描)。
当没有建立聚集索引的时候表的组织结构是堆(heap),而建立聚集索引后表的结构是树(tree)也就是说表中记录是按照聚集索引列的顺序进行物理存放。

to:tangchao515(梦回唐朝) 
您对聚族索引的理解是不恰当的。
首先“索引存储的是数据存放的位置”您可能指的是非聚集索引。
非聚集索引是由:键值、文件号、页号、slot号等构成的。例如执行以下SQL:
select a1 
from t
where a1=1
如果为a1建立了非聚集索引,上面的SQL会直接进行nocluster index seek操作。而不会去扫描
表的数据页。举这个例子是想说明非聚集索引是存放具体键值的。

再有“聚族索引是最快的”。这句话说的太绝对了!
聚集索引叶子节点因为是和表数据页存放在一起,所以进行顺序的返回批量纪录是快的。但是
在进行如上面例子的少量纪录查找的时候,它相对于非聚集索引来说没有优势的,可以说是慢的。由于优化器是CBO,因此在统计值是正确的情况下优化器会选择最优的路径进行执行!器衡量标准是f(cpu,I/O,内存),虽然2005的成本计算方式和2000不同,但是影响成本的因素还是cpu,I/O,内存。提这个主要是说SQL SERVER执行快慢是根据Cost来动态计算的。不能绝对的说“聚族索引是最快的”。

to  andrew_zx() 
浪费的意思是一个表只能建立一个聚集索引(因为表记录只能有一个物理排放顺序)。因此最好建立在需要成批返回的字段上。但是要注意!这个字段最好是静态的,也就是几乎没有update操作的字段。

欢迎大家讨论。

#9


楼上的,是我说的不清楚,存放数据位置的是非聚族索引,不好意思,呵呵。
我认为聚族索引是最快的,其实聚族索引也是个索引,只是它和非聚族索引不同的地方在于叶节点存放的就是数据,所以比非聚族索引要快,非聚族索引找到叶节点后存放的是数据存放的位置,根据这个位置再去找数据,多了一个步骤,所以会慢。
聚族索引的数据是按顺序存放的,如果数据都是唯一的就无法最大体现这个优势,对于那些数据重复比较多的列用聚族索引最好,查询速度最快。

#10


to  tangchao515(梦回唐朝) :

很高兴和您一起讨论。

“非聚族索引找到叶节点后存放的是数据存放的位置,根据这个位置再去找数据,多了一个步骤,所以会慢。”这个理解还是有问题的。
还是上面的例子:
select a1 
from t
where a1=1
如果为a1建立了非聚集索引,上面的SQL会直接进行nocluster index seek操作。
这是我们所说的索引覆盖。索引覆盖的含义是在非聚集索引中可以找到所有所需的数据。
非聚集索引不光存储实际数据记录的位置,还存放字段的字段值!!也就是说如果你为a1建立了
非聚集索引,那么这个非聚集索引中就已经包含了a1字段的所有记录值了!!因此才有了所谓的
索引覆盖。因此类似上面的SQL不会发生查找a1的非聚集索引后再去定位找数据页中a1的值的情况!因为查找非聚集索引比查找表需要更少的I/O,因此这才是非聚集索引的优势。

还有就是“对于那些数据重复比较多的列用聚族索引最好”。这也是误区。因为SQL SERVER的聚集索引和非聚集索引都是树,重复值对树的组织是不利的。Oracle如果是静态表可以建立Bitmap索引,但是SQL SERVER没有这个功能,因此SQL SERVER不建议在有大量重复值的字段上建立索引。

欢迎讨论!

#11


当在非聚集索引中不能完全找到需要的值的时候,才会定位到数据页中,继续查找。

#12


你理解反了吧,非聚集索引中没有包含字段值的,包含的是数据存在的位置,聚集索引是包含了数据值。非聚集索引查找数据先是找到索引的节点,然后根据节点存储的数据位置再去找数据,磁盘I/O要多一些。不过我记性差,也许是我记错了,谁勤快点去查下资料,呵呵。
聚集索引的数据是按顺序存放的,对于相同的数据是连续存放的,所以列值重复比较多的用聚集索引比较好。

#13


已经很久没有静下心来找个技术朋友一起讨论技术了。非常高兴和您讨论。
您可能看了下面的贴子:
http://www.vckbase.com/document/viewdoc/?id=1307

这个贴子的观看率很高,但是其误导率就越大。因为其中很多观点都是错误的!
在这里我想大家进行一下探讨,以让大家对SQL Server索引的概念更加明确。

#14


<-------------------------------
四、其他书上没有的索引使用经验总结

1、用聚合索引比用不是聚合索引的主键速度快
  下面是实例语句:(都是提取25万条数据)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''
使用时间:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000
使用时间:4470毫秒

这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。

------------------------------->

这是贴子上的一个例子。但是请注意他的记录是取得了“都是提取25万条数据”!
表中记录是多少?这种扫描正是聚集索引所擅长的,换句话说这其实是变相的表扫描。
哪能不快!??

再摘录一个:
<-------------------------------
2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi
用时:12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid
用时:18843

  这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。

------------------------------->
这个例子根本就无法说明聚集索引比使用非聚集索引速度快。
首先只要是建立索引的,无论是聚集索引或非聚集索引,该字段都是排序的。
所以对建立索引的字段进行order by 根本就不会有sort操作,因为在索引中已经进行了排序。
而决定快慢的因素还是表中记录数的多少,和查询返回的记录数的多少。
如果表中有30万记录,你要返回25万所有字段的记录集,优化器一般都会选择全表扫描。


#15


其实说非聚集索引是目录不是很准确!应该说是建立索引的字段的快照!原因还是因为非聚集索引存放了字段实际的值!

#16


我同意tangchao515(梦回唐朝)兄的看法,
对非聚集索引进行搜索,找到数据值在表中的位置,然后从该位置直接检索数据。这使非聚集索引成为精确匹配查询的最佳方法,因为索引包含描述查询所搜索的数据值在表中的精确位置的条目。
聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻。例如,如果应用程序执行的一个查询经常检索某一日期范围内的记录,则使用聚集索引可以迅速找到包含开始日期的行,然后检索表中所有相邻的行,直到到达结束日期。这样有助于提高此类查询的性能。同样,如果对从表中检索的数据进行排序时经常要用到某一列,则可以将该表在该列上聚集(物理排序),避免每次查询该列时都进行排序,从而节省成本。

#17


技术讨论总是好事,呵呵,谁对谁错没有关系,错了更好,能多学点东西嘛。
我看过这个文章,基本同意,因为和我的经验比较符合,我以前在电信做的都是千万级数据的处理。根据我的经验大概当查询结果超过总数据的20%时会引起全表扫描,1000万中间查询25万不会引起全表扫描的。
之所以认为聚集索引快的原因我上面也讲了。看了你的观点我倒想起一篇关于索引的blog文章,不知道你是否受那个影响,那上面说聚集索引是文件的扫描,当时也懒的去理他,因为他根本没有搞清楚树是怎么回事。实际上聚集索引和非聚集索引都是用树来存储,只是聚集索引的叶节点存的包括数据,而非聚集索引叶节点存的不包括数据。有聚集索引的表是按顺序存储在数据页中的,非聚集索引是没有按顺序存储的。

#18


还有你一点说的是对的:如果表中有30万记录,你要返回25万所有字段的记录集,优化器一般都会选择全表扫描。但那篇文章里的测试数据是1000万。

#19


这个讨论很好!没有说脏话的,很好!!

其实我没有看到你说的blog。
我所说的都是有依据的。
如果一个表没有建立聚集索引它是堆(Heap),当进行查询的时候如果在非聚集索引中没有找到
全部的信息,如:
select a1,a2 
from t
where a1=1
(为a1建立了非聚集索引),将会在执行nocluster index seek操作后进行RID Lookup。
为什么呢?这是因为a1的字段值可以在a1的非聚集索引中找到,而a2的值却无法找到,而且表也没有建立聚集索引,这个时候就会通过RID定位到数据页上的纪录,查找a2的信息。
当表建立了聚集索引的时候在sql2k中会用Bookmark Lookup代替RID Lookup,因为它已经是树了,定位到聚集索引上就可以找到a2的信息了。而在sql2005中用Clustered index seek代替了Bookmark Lookup,但是本质还是一样的。即:如果是非聚集索引不能找到所有的记录值,将会定位到存储表的数据页上,继续查找。

您说的blog我没看过,但是如果他说的是对聚集索引的扫描就是变相的表扫描的话,我是认同他的观点的!

#20


to  jin2005(小白) 
您说的大部分我是赞同的!但是:

“因为索引包含描述查询所搜索的数据值在表中的精确位置的条目”

非聚集索引中存放“键值、文件号、页号、slot号等”这里的“键值”我指的是数据!!当无法在非聚集索引中找到(注意:这个操作是在存储非聚集索引的数据页中进行的)查询该表的所有字段信息的时候,才会定位到存储表的数据页中对具体记录进行查找。而如何从索引数据页连接到表数据页我在上面给tangchao515(梦回唐朝)的回复中已经提到!
而不是每次都要定位到存储表的数据页中查找。

#21


把默认建立的索引删除,在你最常用的字段上建立索引。至于用聚集还是非聚集索引,查相关资料,一般都是聚集索引。聚集索引就像查字典时用拼音查一样,好像要查一个张(zhang)字,你会“啪”一下把字典翻倒最后几页,而不会从头开始一页一页翻,因为z排在最后,而且z开头的字都挨在一起,你只要在z开头这个范围找就可以了。非聚集索引就像不知道拼音根据部首查一样,但是相同部首的字,却不时连续排列。这就是区别。

#22


看下联机帮助吧:Microsoft&reg; SQL Server&#8482; 2000 将索引组织为 B 树。如果你认为聚集索引扫描就是文件从头到尾的扫描,那说明你对树的概念也不是很清楚,那只能说再去看看数据结构的书了。

#23


还有非聚集索引的叶节点是不保存表里的数据的,这方面去看下索引实现的原理。

#24


to vovo2000(没人要的猫) :

您对聚集索引的描述是正确的。但是对非聚集索引我有不同的观点。

首先明确一个概念:
表记录是要存放在表的数据页中。非聚集索引也要有自己的数据页来存放。在非聚集索引中是按照键值(实际的字段值)来排序的!所以非聚集索引也是排序的!

#25


to tangchao515(梦回唐朝):
对聚集索引的访问是有两种方式的:
clustered index seek和clustered index scan的。前者是进行有限的查找。而后者就有点类似于表扫描,只不过是按照clustered index 的顺序进行。
其实我一直想知道SQL Server对其树结构的具体算法,因为在Oracle中有逆键索引。其就是为了防止键值规律性太强如:1、2、3、4...导致树的左右不平衡而采取的措施。而有的文档宣称SQL Server的索引树结构也是如此,我一直保持怀疑。

另外我要说的是:其实这个贴子是非常有意义的!如果能让广大同行真正搞明白SQL Server的索引问题。也算是为论坛作的一个贡献。

#26


现在回家了,可以找到文档了。其实我们的焦点问题就是在非聚集索引中是否存放了字段值+指针。还是只存放了映射到表中记录的指针。
我摘抄了一下Oracle对索引扫描的描述:

Index Scans
An index scan retrieves data from an index based on the value of one or more
columns in the index. To perform an index scan, Oracle searches the index for the
indexed column values accessed by the statement. If the statement accesses only
columns of the index, then Oracle reads the indexed column values directly from
the index, rather than from the table.

#27


大开眼界

#28


呵呵,虽然我以前一直搞ORACLE,但英语实在太差,查字典又太麻烦。我记性不是很好,但记得聚集索引和非聚集索引的区别在于,聚集索引的叶节点保存的数据页就是表的数据,而非聚集索引页节点的数据页保存的是表数据存放的物理位置,然后根据这个信息去查找表数据,那哪个快哪个慢就很容易得出结论了。这个可以去看下大学里的数据结构书,书上应该有讲的。也正因为有聚集索引的表数据是连续存放的,所以我认为列值重复比较多的建聚集索引更有效,因为列值一样的数据是存放在一起的。

#29


讨论精彩呀

#30


A:非聚集索引建议使用条件:
在创建非聚集索引之前,应先了解您的数据是如何被访问的。可考虑将非聚集索引用于: 

包含大量非重复值的列,如姓氏和名字的组合(如果聚集索引用于其它列)。如果只有很少的非重复值,如只有 1 和 0,则大多数查询将不使用索引,因为此时表扫描通常更有效。


不返回大型结果集的查询。


返回精确匹配的查询的搜索条件(WHERE 子句)中经常使用的列。


经常需要联接和分组的决策支持系统应用程序。应在联接和分组操作中使用的列上创建多个非聚集索引,在任何外键列上创建一个聚集索引。


在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。
 
B:聚集索引建议使用条件:

在创建聚集索引之前,应先了解您的数据是如何被访问的。可考虑将聚集索引用于: 

包含大量非重复值的列。


使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。


被连续访问的列。


返回大型结果集的查询。


经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。


OLTP 类型的应用程序,这些程序要求进行非常快速的单行查找(一般通过主键)。应在主键上创建聚集索引。 
聚集索引不适用于: 

频繁更改的列 
这将导致整行移动(因为 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,因为在大数据量事务处理系统中数据是易失的。

宽键 
来自聚集索引的键值由所有非聚集索引作为查找键使用,因此存储在每个非聚集索引的叶条目内。

#31


摘抄了SQL Server2000的帮助文档:
<------------------
非聚集索引
非聚集索引与聚集索引一样有 B 树结构,但是有两个重大差别: 

数据行不按非聚集索引键的顺序排序和存储。


非聚集索引的叶层不包含数据页。 
相反,叶节点包含索引行。每个索引行包含非聚集键值以及一个或多个行定位器,这些行定位器指向有该键值的数据行(如果索引不唯一,则可能是多行)。
------------------------->

在这里关键的问题是“每个索引行包含非聚集键值以及一个或多个行定位器”的“非聚集键值”中是否包含字段数据。
如果SQL Server的帮助像Oracle一样明确的写明,可能就没有这场讨论了。但是我很久以前看过微软的培训PPT上面明确
写明了这个概念。我们可以从别的角度考虑一下这个问题:
树的构建一定要有相应的值作为排列依据,如果不用字段内容作为依据那需要什么呢?如果说用该字段内容的存储地址也是可以说过去的。但是那为什么非聚集索引要有每个单个索引的单个字段或字段组合不能超过900byte的限制呢?既然存储的是地址应该所有单个字段的非聚集索引大小差不多才对呀,为什么字段内容多的非聚集索引数据量大呢?我可以肯定:如果一个表中的所有列都可以建立非聚集索引,而为每个字段都单独建立索引的话,索引的总数据量会超过表的总数据量。因为索引中不光包含字段的值,还包括指向具体记录的指针。如果用粗浅的公式表示:
表数据量=sum(字段)
索引数据量=sum(字段)+sum(指针)

欢迎大家继续讨论!!

#32


zlp321002(恰似一江春水向东流)引用的帮助中: 
"在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。"
就是对
select a1
from t
where a1=1  --a1建立了非聚集索引
这类SQL的诠释。

#33


不是这样理解的,索引叶节点包含的不是字段值,但叶节点指向的数据页包含了字段值,先是找到页节点,然后在叶节点指向的数据页扫描字段值是否和条件匹配。所以字段长度越大一个数据页包含的字段数量就越小,整个索引文件也就越大。
这样吧,不要根据字面理解去想当然,去看下数据结构的书就明白了啊,呵呵。基础的东西虽然枯燥,但还是很必要的。当然了解基础知识不一定是高手,但要想成为高手一定是要去了解基础知识的。

#34


我这个人怕麻烦,也懒的打字,很多地方没有说清楚,只能抱歉了,有兴趣研究的话去翻翻书吧,我也是从书上学的,呵呵。

#35


支持!!

#36


SQL2k帮助中“设计索引”的摘录:
“覆盖的查询可以提高性能。覆盖的查询是指查询中所有指定的列都包含在同一个索引中。例如,如果在一个表的 a、b 和 c 列上创建了组合索引,则从该表中检索 a 和 b 列的查询被视为覆盖的查询。创建覆盖一个查询的索引可以提高性能,因为该查询的所有数据都包含在索引自身当中;检索数据时只需引用表的索引页,不必引用数据页,因而减少了 I/O 总量。尽管给索引添加列以覆盖查询可以提高性能,但在索引中额外维护更多的列会产生更新和存储成本。”

在SQL 2005帮助中“Index Basics ”(url: http://msdn2.microsoft.com/en-us/library/ms190457.aspx)
的摘录:

Nonclustered 

Nonclustered indexes have a structure separate from the data rows. A nonclustered index contains the nonclustered index key values and each key value entry has a pointer to the data row that contains the key value. 

#37


我觉得上面的描述已经足够证明非聚集索引的键值包含字段值。
您让我看数据结构的书籍,非常感谢。我看了B树的遍例和调整规则。但是却不明白这与我们的讨论有什么联系?!

希望更多的同行加入讨论。

#38


建索引要看情况的

#39


晕,索引当然包括键值了,要不怎么查找比较啊?但不是你想像的那样。唉,一两句也说不清楚,包括树、B树、索引实现的原理。让你看树是因为你说聚集索引就是文件的遍历,所以让你去看下树是怎么存储数据和怎么查找数据的。要都叫我讲我可懒的打字,呵呵,对不起啊,论坛毕竟不是课堂。

#40


MARK

#41


其实借用这个贴子讨论的目的只是为了明确非聚集索引和聚集索引的概念。我想这个目的已经达到了。在这里总结一下:

1、非聚集索引中不光保存指向表或聚集索引的指针,也真实的存放了其对应的字段值。当查询可以在非聚集索引中找到所有信息的时候,是不需要定位到存储表的数据页中再进行查找的(这就是索引覆盖)。当有些字段不能在索引中完全找到的时候才定位到表数据页中再进行查找。

2、聚集索引在返回大量连续成批的记录要比非聚集索引有优势。单独查询某一两条记录的时候聚集索引不会比非聚集索引有优势。不要迷信执行时间,看看你的执行计划是不是合理才是好方法。

3、关于原理和数据结构的说明:
   几乎所有的计算机概念都是由数学模型衍生出来的。我们的关系数据库是以关系代数作为数学基础理论衍生出来的。我们的B树等数据结构模型也是有数学基础的。但是软件产品公司会根据实际的需求改进和扩展这些数学模型。就像执行计划中的很多逻辑操作符是对应关系代数运算符的,但是物理操作符才是实际的执行方式(扩展出来的)。

4、“让你看树是因为你说聚集索引就是文件的遍历”在上面的言论中我没有说过“聚集索引就是文件的遍历”。因为无论数据库的何种对象只要是持久化到硬盘上的,都要存储在文件中。这句话太空,我不会这么说。

5、关于您的“要都叫我讲我可懒的打字,呵呵,对不起啊,论坛毕竟不是课堂。”
我想不必麻烦您为我在这里做什么具体培训。我从sql6.5到sql2005已经有5年的专职DBA经历(当然其中也主要针对Oracle)。我感觉在线帮助我已经学习不过来了。^O^


最后,希望所有看过这个贴子的csdner,仔细的察看一下在线文档后再来判断这场讨论的众多观点的正确性。希望我们共同进步。

楼主请封贴吧!

#42


很少看到这样的技术讨论,
不错,学术气氛很好。感觉这样才是搞技术的样子。

TO:tangchao515(梦回唐朝) 
给个建议,不要动不动就叫别人去看某某书,那么厚的一本书,看完了,讨论的气氛也没了。
懂的东西就拿出来吧。

有争议的讨论,
不懂的学习,
懂的复习。

#43


不是不想封贴,现在各执一词,讨论得有个正确结论不是吗?!

现在这种讨论情况已经很少见了,大家都有各自的观点。

欢迎继续讨论!

#44


索引在数据库中是值得讨论..

#45


学习中

#46


学习

#47


ashzs((可以包含中文字符)) 兄的观点比较正确。
我试了一下,对于一个有100万条的记录表,
如果select a1 from 表where a1>某值,
对于a1建非聚集索引时,效率比建聚集索引要高。
证明《索引覆盖》的观点正确,即如果你只是select的只是建了索引那个字段,那么非聚集的要比聚集的快!因为数据量小,非聚集的查询等于只在索引表中进行,而这个表相对于聚集的,要小。

#48


大家是怎么看到这个执行的时间的??

#49


不好意思,不是想显示自己怎么样,实际上我没有具体搞技术也好几年了,最近因为换了工作又开始搞数据库,在这儿还是学习了很多东西,回忆起了很多遗忘的东西。只是因为自己实在是太懒,看来这个毛病也该改下了。
来这儿讨论主要是学习技术,大家共同提高,至于谁对谁错并不重要,在工作中我现在也多是给予下面的人指导,很多实际的细节还经常被他们取笑,呵呵,可能习惯了给他们个方向然后让他们自己去看书解决,在这儿也这样,实在不好意思,有时间我自己翻资料去确认下。
事实上这个帖子也让自己想起了以前已经忘记的东西,象上次有个帖子,我认为数值型和字符型建索引效率区别不大,但这个帖子让我想起来了索引的原理,应该还是数值型的效率高。
最后感谢所以参与讨论和批评我的兄弟,大家以后交个朋友嘛。

TO:andrew_zx

看执行时间的代码
declare @d datetime
set @d=getdate()

要执行的SQL

select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

#50


呵呵,讨论的很精彩!

#1


索引啊

#2


SQLSERVER的主键缺省是聚族索引,效率是最高的,但普通的查询感觉不出差别。而且我认为如果序号这样数据唯一的列用聚族索引有些浪费,因为一个表只能有一个聚族索引,要好好利用。

#3


索引

#4


主键默认索引其实是聚集索引,其适合于返回大批连续记录。它的执行机制其实就是变相的表扫描。如果根据序号查询某条具体记录,还是非聚集索引要快!因为不用检索数据页,可以减少I/O。最好看看你的执行计划。

#5


你在这个序号列上创建主键时,sql server会自动把这个序号列加建一个聚族索引.
聚族索引就像书的目录一样.我认在序号列创建聚族索引意思不好.因为你每一个行就是书的目录了.那这样的目录有什么用了.
一个表只能有一个聚族索引,要好好利用

#6


ashzs对聚族索引的理解可能不是很恰当吧,聚族索引是最快的,因为他在叶节点直接就存储的是数据,而索引存储的是数据存放的位置,等于要多一个步骤才能获得数据。

#7


回:tangchao515(梦回唐朝) 

既然是最快的,为什么把唯一列做为主键(聚集索引)会浪费呢?

#8


to:wangkenping(找有感觉的妹妹) 
您对聚集索引的理解有些问题。
非聚集索引才是像目录一样。聚集索引的叶子节点是存放在数据页中的,其实就是和表数据放在了一起。所以说扫描聚集索引和表扫描(注意这里没有说全表扫描)。
当没有建立聚集索引的时候表的组织结构是堆(heap),而建立聚集索引后表的结构是树(tree)也就是说表中记录是按照聚集索引列的顺序进行物理存放。

to:tangchao515(梦回唐朝) 
您对聚族索引的理解是不恰当的。
首先“索引存储的是数据存放的位置”您可能指的是非聚集索引。
非聚集索引是由:键值、文件号、页号、slot号等构成的。例如执行以下SQL:
select a1 
from t
where a1=1
如果为a1建立了非聚集索引,上面的SQL会直接进行nocluster index seek操作。而不会去扫描
表的数据页。举这个例子是想说明非聚集索引是存放具体键值的。

再有“聚族索引是最快的”。这句话说的太绝对了!
聚集索引叶子节点因为是和表数据页存放在一起,所以进行顺序的返回批量纪录是快的。但是
在进行如上面例子的少量纪录查找的时候,它相对于非聚集索引来说没有优势的,可以说是慢的。由于优化器是CBO,因此在统计值是正确的情况下优化器会选择最优的路径进行执行!器衡量标准是f(cpu,I/O,内存),虽然2005的成本计算方式和2000不同,但是影响成本的因素还是cpu,I/O,内存。提这个主要是说SQL SERVER执行快慢是根据Cost来动态计算的。不能绝对的说“聚族索引是最快的”。

to  andrew_zx() 
浪费的意思是一个表只能建立一个聚集索引(因为表记录只能有一个物理排放顺序)。因此最好建立在需要成批返回的字段上。但是要注意!这个字段最好是静态的,也就是几乎没有update操作的字段。

欢迎大家讨论。

#9


楼上的,是我说的不清楚,存放数据位置的是非聚族索引,不好意思,呵呵。
我认为聚族索引是最快的,其实聚族索引也是个索引,只是它和非聚族索引不同的地方在于叶节点存放的就是数据,所以比非聚族索引要快,非聚族索引找到叶节点后存放的是数据存放的位置,根据这个位置再去找数据,多了一个步骤,所以会慢。
聚族索引的数据是按顺序存放的,如果数据都是唯一的就无法最大体现这个优势,对于那些数据重复比较多的列用聚族索引最好,查询速度最快。

#10


to  tangchao515(梦回唐朝) :

很高兴和您一起讨论。

“非聚族索引找到叶节点后存放的是数据存放的位置,根据这个位置再去找数据,多了一个步骤,所以会慢。”这个理解还是有问题的。
还是上面的例子:
select a1 
from t
where a1=1
如果为a1建立了非聚集索引,上面的SQL会直接进行nocluster index seek操作。
这是我们所说的索引覆盖。索引覆盖的含义是在非聚集索引中可以找到所有所需的数据。
非聚集索引不光存储实际数据记录的位置,还存放字段的字段值!!也就是说如果你为a1建立了
非聚集索引,那么这个非聚集索引中就已经包含了a1字段的所有记录值了!!因此才有了所谓的
索引覆盖。因此类似上面的SQL不会发生查找a1的非聚集索引后再去定位找数据页中a1的值的情况!因为查找非聚集索引比查找表需要更少的I/O,因此这才是非聚集索引的优势。

还有就是“对于那些数据重复比较多的列用聚族索引最好”。这也是误区。因为SQL SERVER的聚集索引和非聚集索引都是树,重复值对树的组织是不利的。Oracle如果是静态表可以建立Bitmap索引,但是SQL SERVER没有这个功能,因此SQL SERVER不建议在有大量重复值的字段上建立索引。

欢迎讨论!

#11


当在非聚集索引中不能完全找到需要的值的时候,才会定位到数据页中,继续查找。

#12


你理解反了吧,非聚集索引中没有包含字段值的,包含的是数据存在的位置,聚集索引是包含了数据值。非聚集索引查找数据先是找到索引的节点,然后根据节点存储的数据位置再去找数据,磁盘I/O要多一些。不过我记性差,也许是我记错了,谁勤快点去查下资料,呵呵。
聚集索引的数据是按顺序存放的,对于相同的数据是连续存放的,所以列值重复比较多的用聚集索引比较好。

#13


已经很久没有静下心来找个技术朋友一起讨论技术了。非常高兴和您讨论。
您可能看了下面的贴子:
http://www.vckbase.com/document/viewdoc/?id=1307

这个贴子的观看率很高,但是其误导率就越大。因为其中很多观点都是错误的!
在这里我想大家进行一下探讨,以让大家对SQL Server索引的概念更加明确。

#14


<-------------------------------
四、其他书上没有的索引使用经验总结

1、用聚合索引比用不是聚合索引的主键速度快
  下面是实例语句:(都是提取25万条数据)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''
使用时间:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000
使用时间:4470毫秒

这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。

------------------------------->

这是贴子上的一个例子。但是请注意他的记录是取得了“都是提取25万条数据”!
表中记录是多少?这种扫描正是聚集索引所擅长的,换句话说这其实是变相的表扫描。
哪能不快!??

再摘录一个:
<-------------------------------
2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi
用时:12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid
用时:18843

  这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。

------------------------------->
这个例子根本就无法说明聚集索引比使用非聚集索引速度快。
首先只要是建立索引的,无论是聚集索引或非聚集索引,该字段都是排序的。
所以对建立索引的字段进行order by 根本就不会有sort操作,因为在索引中已经进行了排序。
而决定快慢的因素还是表中记录数的多少,和查询返回的记录数的多少。
如果表中有30万记录,你要返回25万所有字段的记录集,优化器一般都会选择全表扫描。


#15


其实说非聚集索引是目录不是很准确!应该说是建立索引的字段的快照!原因还是因为非聚集索引存放了字段实际的值!

#16


我同意tangchao515(梦回唐朝)兄的看法,
对非聚集索引进行搜索,找到数据值在表中的位置,然后从该位置直接检索数据。这使非聚集索引成为精确匹配查询的最佳方法,因为索引包含描述查询所搜索的数据值在表中的精确位置的条目。
聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻。例如,如果应用程序执行的一个查询经常检索某一日期范围内的记录,则使用聚集索引可以迅速找到包含开始日期的行,然后检索表中所有相邻的行,直到到达结束日期。这样有助于提高此类查询的性能。同样,如果对从表中检索的数据进行排序时经常要用到某一列,则可以将该表在该列上聚集(物理排序),避免每次查询该列时都进行排序,从而节省成本。

#17


技术讨论总是好事,呵呵,谁对谁错没有关系,错了更好,能多学点东西嘛。
我看过这个文章,基本同意,因为和我的经验比较符合,我以前在电信做的都是千万级数据的处理。根据我的经验大概当查询结果超过总数据的20%时会引起全表扫描,1000万中间查询25万不会引起全表扫描的。
之所以认为聚集索引快的原因我上面也讲了。看了你的观点我倒想起一篇关于索引的blog文章,不知道你是否受那个影响,那上面说聚集索引是文件的扫描,当时也懒的去理他,因为他根本没有搞清楚树是怎么回事。实际上聚集索引和非聚集索引都是用树来存储,只是聚集索引的叶节点存的包括数据,而非聚集索引叶节点存的不包括数据。有聚集索引的表是按顺序存储在数据页中的,非聚集索引是没有按顺序存储的。

#18


还有你一点说的是对的:如果表中有30万记录,你要返回25万所有字段的记录集,优化器一般都会选择全表扫描。但那篇文章里的测试数据是1000万。

#19


这个讨论很好!没有说脏话的,很好!!

其实我没有看到你说的blog。
我所说的都是有依据的。
如果一个表没有建立聚集索引它是堆(Heap),当进行查询的时候如果在非聚集索引中没有找到
全部的信息,如:
select a1,a2 
from t
where a1=1
(为a1建立了非聚集索引),将会在执行nocluster index seek操作后进行RID Lookup。
为什么呢?这是因为a1的字段值可以在a1的非聚集索引中找到,而a2的值却无法找到,而且表也没有建立聚集索引,这个时候就会通过RID定位到数据页上的纪录,查找a2的信息。
当表建立了聚集索引的时候在sql2k中会用Bookmark Lookup代替RID Lookup,因为它已经是树了,定位到聚集索引上就可以找到a2的信息了。而在sql2005中用Clustered index seek代替了Bookmark Lookup,但是本质还是一样的。即:如果是非聚集索引不能找到所有的记录值,将会定位到存储表的数据页上,继续查找。

您说的blog我没看过,但是如果他说的是对聚集索引的扫描就是变相的表扫描的话,我是认同他的观点的!

#20


to  jin2005(小白) 
您说的大部分我是赞同的!但是:

“因为索引包含描述查询所搜索的数据值在表中的精确位置的条目”

非聚集索引中存放“键值、文件号、页号、slot号等”这里的“键值”我指的是数据!!当无法在非聚集索引中找到(注意:这个操作是在存储非聚集索引的数据页中进行的)查询该表的所有字段信息的时候,才会定位到存储表的数据页中对具体记录进行查找。而如何从索引数据页连接到表数据页我在上面给tangchao515(梦回唐朝)的回复中已经提到!
而不是每次都要定位到存储表的数据页中查找。

#21


把默认建立的索引删除,在你最常用的字段上建立索引。至于用聚集还是非聚集索引,查相关资料,一般都是聚集索引。聚集索引就像查字典时用拼音查一样,好像要查一个张(zhang)字,你会“啪”一下把字典翻倒最后几页,而不会从头开始一页一页翻,因为z排在最后,而且z开头的字都挨在一起,你只要在z开头这个范围找就可以了。非聚集索引就像不知道拼音根据部首查一样,但是相同部首的字,却不时连续排列。这就是区别。

#22


看下联机帮助吧:Microsoft&reg; SQL Server&#8482; 2000 将索引组织为 B 树。如果你认为聚集索引扫描就是文件从头到尾的扫描,那说明你对树的概念也不是很清楚,那只能说再去看看数据结构的书了。

#23


还有非聚集索引的叶节点是不保存表里的数据的,这方面去看下索引实现的原理。

#24


to vovo2000(没人要的猫) :

您对聚集索引的描述是正确的。但是对非聚集索引我有不同的观点。

首先明确一个概念:
表记录是要存放在表的数据页中。非聚集索引也要有自己的数据页来存放。在非聚集索引中是按照键值(实际的字段值)来排序的!所以非聚集索引也是排序的!

#25


to tangchao515(梦回唐朝):
对聚集索引的访问是有两种方式的:
clustered index seek和clustered index scan的。前者是进行有限的查找。而后者就有点类似于表扫描,只不过是按照clustered index 的顺序进行。
其实我一直想知道SQL Server对其树结构的具体算法,因为在Oracle中有逆键索引。其就是为了防止键值规律性太强如:1、2、3、4...导致树的左右不平衡而采取的措施。而有的文档宣称SQL Server的索引树结构也是如此,我一直保持怀疑。

另外我要说的是:其实这个贴子是非常有意义的!如果能让广大同行真正搞明白SQL Server的索引问题。也算是为论坛作的一个贡献。

#26


现在回家了,可以找到文档了。其实我们的焦点问题就是在非聚集索引中是否存放了字段值+指针。还是只存放了映射到表中记录的指针。
我摘抄了一下Oracle对索引扫描的描述:

Index Scans
An index scan retrieves data from an index based on the value of one or more
columns in the index. To perform an index scan, Oracle searches the index for the
indexed column values accessed by the statement. If the statement accesses only
columns of the index, then Oracle reads the indexed column values directly from
the index, rather than from the table.

#27


大开眼界

#28


呵呵,虽然我以前一直搞ORACLE,但英语实在太差,查字典又太麻烦。我记性不是很好,但记得聚集索引和非聚集索引的区别在于,聚集索引的叶节点保存的数据页就是表的数据,而非聚集索引页节点的数据页保存的是表数据存放的物理位置,然后根据这个信息去查找表数据,那哪个快哪个慢就很容易得出结论了。这个可以去看下大学里的数据结构书,书上应该有讲的。也正因为有聚集索引的表数据是连续存放的,所以我认为列值重复比较多的建聚集索引更有效,因为列值一样的数据是存放在一起的。

#29


讨论精彩呀

#30


A:非聚集索引建议使用条件:
在创建非聚集索引之前,应先了解您的数据是如何被访问的。可考虑将非聚集索引用于: 

包含大量非重复值的列,如姓氏和名字的组合(如果聚集索引用于其它列)。如果只有很少的非重复值,如只有 1 和 0,则大多数查询将不使用索引,因为此时表扫描通常更有效。


不返回大型结果集的查询。


返回精确匹配的查询的搜索条件(WHERE 子句)中经常使用的列。


经常需要联接和分组的决策支持系统应用程序。应在联接和分组操作中使用的列上创建多个非聚集索引,在任何外键列上创建一个聚集索引。


在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。
 
B:聚集索引建议使用条件:

在创建聚集索引之前,应先了解您的数据是如何被访问的。可考虑将聚集索引用于: 

包含大量非重复值的列。


使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。


被连续访问的列。


返回大型结果集的查询。


经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。


OLTP 类型的应用程序,这些程序要求进行非常快速的单行查找(一般通过主键)。应在主键上创建聚集索引。 
聚集索引不适用于: 

频繁更改的列 
这将导致整行移动(因为 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,因为在大数据量事务处理系统中数据是易失的。

宽键 
来自聚集索引的键值由所有非聚集索引作为查找键使用,因此存储在每个非聚集索引的叶条目内。

#31


摘抄了SQL Server2000的帮助文档:
<------------------
非聚集索引
非聚集索引与聚集索引一样有 B 树结构,但是有两个重大差别: 

数据行不按非聚集索引键的顺序排序和存储。


非聚集索引的叶层不包含数据页。 
相反,叶节点包含索引行。每个索引行包含非聚集键值以及一个或多个行定位器,这些行定位器指向有该键值的数据行(如果索引不唯一,则可能是多行)。
------------------------->

在这里关键的问题是“每个索引行包含非聚集键值以及一个或多个行定位器”的“非聚集键值”中是否包含字段数据。
如果SQL Server的帮助像Oracle一样明确的写明,可能就没有这场讨论了。但是我很久以前看过微软的培训PPT上面明确
写明了这个概念。我们可以从别的角度考虑一下这个问题:
树的构建一定要有相应的值作为排列依据,如果不用字段内容作为依据那需要什么呢?如果说用该字段内容的存储地址也是可以说过去的。但是那为什么非聚集索引要有每个单个索引的单个字段或字段组合不能超过900byte的限制呢?既然存储的是地址应该所有单个字段的非聚集索引大小差不多才对呀,为什么字段内容多的非聚集索引数据量大呢?我可以肯定:如果一个表中的所有列都可以建立非聚集索引,而为每个字段都单独建立索引的话,索引的总数据量会超过表的总数据量。因为索引中不光包含字段的值,还包括指向具体记录的指针。如果用粗浅的公式表示:
表数据量=sum(字段)
索引数据量=sum(字段)+sum(指针)

欢迎大家继续讨论!!

#32


zlp321002(恰似一江春水向东流)引用的帮助中: 
"在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。"
就是对
select a1
from t
where a1=1  --a1建立了非聚集索引
这类SQL的诠释。

#33


不是这样理解的,索引叶节点包含的不是字段值,但叶节点指向的数据页包含了字段值,先是找到页节点,然后在叶节点指向的数据页扫描字段值是否和条件匹配。所以字段长度越大一个数据页包含的字段数量就越小,整个索引文件也就越大。
这样吧,不要根据字面理解去想当然,去看下数据结构的书就明白了啊,呵呵。基础的东西虽然枯燥,但还是很必要的。当然了解基础知识不一定是高手,但要想成为高手一定是要去了解基础知识的。

#34


我这个人怕麻烦,也懒的打字,很多地方没有说清楚,只能抱歉了,有兴趣研究的话去翻翻书吧,我也是从书上学的,呵呵。

#35


支持!!

#36


SQL2k帮助中“设计索引”的摘录:
“覆盖的查询可以提高性能。覆盖的查询是指查询中所有指定的列都包含在同一个索引中。例如,如果在一个表的 a、b 和 c 列上创建了组合索引,则从该表中检索 a 和 b 列的查询被视为覆盖的查询。创建覆盖一个查询的索引可以提高性能,因为该查询的所有数据都包含在索引自身当中;检索数据时只需引用表的索引页,不必引用数据页,因而减少了 I/O 总量。尽管给索引添加列以覆盖查询可以提高性能,但在索引中额外维护更多的列会产生更新和存储成本。”

在SQL 2005帮助中“Index Basics ”(url: http://msdn2.microsoft.com/en-us/library/ms190457.aspx)
的摘录:

Nonclustered 

Nonclustered indexes have a structure separate from the data rows. A nonclustered index contains the nonclustered index key values and each key value entry has a pointer to the data row that contains the key value. 

#37


我觉得上面的描述已经足够证明非聚集索引的键值包含字段值。
您让我看数据结构的书籍,非常感谢。我看了B树的遍例和调整规则。但是却不明白这与我们的讨论有什么联系?!

希望更多的同行加入讨论。

#38


建索引要看情况的

#39


晕,索引当然包括键值了,要不怎么查找比较啊?但不是你想像的那样。唉,一两句也说不清楚,包括树、B树、索引实现的原理。让你看树是因为你说聚集索引就是文件的遍历,所以让你去看下树是怎么存储数据和怎么查找数据的。要都叫我讲我可懒的打字,呵呵,对不起啊,论坛毕竟不是课堂。

#40


MARK

#41


其实借用这个贴子讨论的目的只是为了明确非聚集索引和聚集索引的概念。我想这个目的已经达到了。在这里总结一下:

1、非聚集索引中不光保存指向表或聚集索引的指针,也真实的存放了其对应的字段值。当查询可以在非聚集索引中找到所有信息的时候,是不需要定位到存储表的数据页中再进行查找的(这就是索引覆盖)。当有些字段不能在索引中完全找到的时候才定位到表数据页中再进行查找。

2、聚集索引在返回大量连续成批的记录要比非聚集索引有优势。单独查询某一两条记录的时候聚集索引不会比非聚集索引有优势。不要迷信执行时间,看看你的执行计划是不是合理才是好方法。

3、关于原理和数据结构的说明:
   几乎所有的计算机概念都是由数学模型衍生出来的。我们的关系数据库是以关系代数作为数学基础理论衍生出来的。我们的B树等数据结构模型也是有数学基础的。但是软件产品公司会根据实际的需求改进和扩展这些数学模型。就像执行计划中的很多逻辑操作符是对应关系代数运算符的,但是物理操作符才是实际的执行方式(扩展出来的)。

4、“让你看树是因为你说聚集索引就是文件的遍历”在上面的言论中我没有说过“聚集索引就是文件的遍历”。因为无论数据库的何种对象只要是持久化到硬盘上的,都要存储在文件中。这句话太空,我不会这么说。

5、关于您的“要都叫我讲我可懒的打字,呵呵,对不起啊,论坛毕竟不是课堂。”
我想不必麻烦您为我在这里做什么具体培训。我从sql6.5到sql2005已经有5年的专职DBA经历(当然其中也主要针对Oracle)。我感觉在线帮助我已经学习不过来了。^O^


最后,希望所有看过这个贴子的csdner,仔细的察看一下在线文档后再来判断这场讨论的众多观点的正确性。希望我们共同进步。

楼主请封贴吧!

#42


很少看到这样的技术讨论,
不错,学术气氛很好。感觉这样才是搞技术的样子。

TO:tangchao515(梦回唐朝) 
给个建议,不要动不动就叫别人去看某某书,那么厚的一本书,看完了,讨论的气氛也没了。
懂的东西就拿出来吧。

有争议的讨论,
不懂的学习,
懂的复习。

#43


不是不想封贴,现在各执一词,讨论得有个正确结论不是吗?!

现在这种讨论情况已经很少见了,大家都有各自的观点。

欢迎继续讨论!

#44


索引在数据库中是值得讨论..

#45


学习中

#46


学习

#47


ashzs((可以包含中文字符)) 兄的观点比较正确。
我试了一下,对于一个有100万条的记录表,
如果select a1 from 表where a1>某值,
对于a1建非聚集索引时,效率比建聚集索引要高。
证明《索引覆盖》的观点正确,即如果你只是select的只是建了索引那个字段,那么非聚集的要比聚集的快!因为数据量小,非聚集的查询等于只在索引表中进行,而这个表相对于聚集的,要小。

#48


大家是怎么看到这个执行的时间的??

#49


不好意思,不是想显示自己怎么样,实际上我没有具体搞技术也好几年了,最近因为换了工作又开始搞数据库,在这儿还是学习了很多东西,回忆起了很多遗忘的东西。只是因为自己实在是太懒,看来这个毛病也该改下了。
来这儿讨论主要是学习技术,大家共同提高,至于谁对谁错并不重要,在工作中我现在也多是给予下面的人指导,很多实际的细节还经常被他们取笑,呵呵,可能习惯了给他们个方向然后让他们自己去看书解决,在这儿也这样,实在不好意思,有时间我自己翻资料去确认下。
事实上这个帖子也让自己想起了以前已经忘记的东西,象上次有个帖子,我认为数值型和字符型建索引效率区别不大,但这个帖子让我想起来了索引的原理,应该还是数值型的效率高。
最后感谢所以参与讨论和批评我的兄弟,大家以后交个朋友嘛。

TO:andrew_zx

看执行时间的代码
declare @d datetime
set @d=getdate()

要执行的SQL

select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

#50


呵呵,讨论的很精彩!