我应该在SQL Server中索引一个位域吗?

我记得在某一点上读到一个低基数的字段(数量很less)是不值得的。 我承认,我不太了解指数如何工作来理解这是为什么。

那么如果我有一个有1亿行的表格,而且我正在select位域为1的logging呢? 假设在任何时候,位字段为1(而不是0)的logging只有一小部分。 是否值得索引该位字段? 为什么?

当然,我可以testing它,检查执行计划,我会这样做,但我也很好奇它背后的理论。 基数何时起作用?何时不起作用?

考虑SQL中的索引是什么 – 索引实际上是指向其他内存块(即指向行的指针)的内存块。 索引被分解为多个页面,以便部分索引可以根据使用情况从内存中加载和卸载。

当您询问一组行时,SQL使用索引比表扫描更快地查找行(查看每一行)。

SQL有聚簇索引和非聚簇索引。 我对聚簇索引的理解是,它们将相似的索引值分组到相同的页面中。 这样,当你要求所有的行匹配一个索引值时,SQL可以从一个聚集的内存页面返回这些行。 这就是为什么尝试群集索引一个GUID列是一个坏主意 – 你不要尝试群集随机值。

索引整数列时,SQL的索引包含每个索引值的一组行。 如果你有一个1到10的范围,那么你将有10个索引指针。 取决于有多less行,这可以分页。 如果查询查找匹配“1”的索引,然后Name包含“Fred”(假设Name列未被索引),则SQL快速获取与“1”匹配的行集合,然后通过表扫描find其余部分。

所以SQL实际上是在试图减less它必须迭代的工作集(行数)。

当你索引一个位域(或者一些窄范围)时,你只能通过匹配该值的行数来减less工作集。 如果你有less量的行匹配会减less你的工作集很多。 对于拥有50/50分布的大量行,与保持索引保持最新状态相比,它可能会给您带来很less的性能提升。

每个人都说要testing的原因是,SQL包含一个非常聪明和复杂的优化器,如果决定表扫描速度更快,或者可能使用sorting,或者可能组织内存页面,可能会忽略索引。

我刚刚通过另一个方式来看到这个问题。 假设你的陈述只有less数logging假定值为1(那些是你感兴趣的),那么过滤掉的索引可能是一个不错的select。 就像是:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1 

这将创build一个非常小的索引,当查询中的谓词时,优化器足够聪明。

只有less数几个比特字段设置为1的1亿条logging? 是的,我认为索引位字段肯定会加速查询bit = 1logging。 您应该从索引获得对数search时间,然后只触摸位数为1的logging的几页。 否则,您必须触摸1亿logging表的所有页面。

然后,我绝对不是数据库专家,可能会错过重要的东西。

虽然我不认为我会自己索引一列,但是将位列作为复合索引的一部分是很常见的。

一个简单的例子就是一个关于ACTIVE,LASTNAME的索引,而不仅仅是姓氏,当你的应用程序几乎总是在寻找活跃的客户。

如果你还没有阅读,Jason Massie最近写了一篇文章,讨论这个话题。

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

编辑:新文章的位置 – http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Wayback机器以前的“新”文章位置: http : //web.archive.org/web/20120201122503/http : //sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

新的SQL Server Pedia位置是Toadworld,它有Kenneth Fisher的一篇新文章,讨论这个话题:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be- used.aspx

当然,这是值得的,特别是如果你需要通过这个值检索数据。 这将类似于使用稀疏matrix而不是使用正常matrix。

现在使用SQL 2008,您可以使用分区function,并且可以过滤索引中的数据。 较早的版本的缺点是索引将被用于所有的数据,但是这可以通过将有趣的值存储在单独的文件组中来优化。

正如其他人所说,你会想要衡量这一点。 我不记得我在哪里阅读过这篇文章,但是一个专栏需要有很高的基数(大约95%)才能使索引有效。 您最好的testing是build立索引并检查BIT字段的0和1值的执行计划。 如果您在执行计划中看到索引查找操作,那么您知道将使用您的索引。

你最好的做法是用一个基本的SELECT * FROM表testingWHERE BitField = 1; 查询并逐步从中逐步build立function,直到您对应用程序有一个实际的查询,检查每一步的执行计划以确保索引查找仍在使用中。 无可否认,这个执行计划不能保证在生产中使用,但是很有可能。

一些信息可以在sql-server-performance.com论坛和参考文章中find

“我记得在某一点上读到一个低基数(less数不同值)的领域是不值得的”

这是因为SQL Server几乎总能find更有效的方式来执行表扫描而不是读取索引。 所以基本上你的索引永远不会被使用,维护它是一种浪费。 正如其他人所说,在复合指数中可能是可以的。

如果您的目标是查询位域值快于1的logging,则可以尝试使用只包含位域等于“1”的logging的基表的索引视图。 在企业版中,如果查询可以使用索引视图而不是指定的表来提高查询性能,则将使用该视图。 理论上这会增加select查询的速度,这些查询只查找字段值为“1”的logging。

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

所有这些都假设你是Microsoft SQL Server 2005 Enterprise。 2008年也可能适用,我对这个版本并不熟悉。

如果您的发行版已知并且不平衡,例如,99%的行是bit = 1,1%是bit = 0,那么当您执行位= 1的WHERE子句时,全表扫描将大约与索引扫描。 如果你想有一个快速查询位= 0,我知道的最好的方法是创build一个过滤的索引,添加一个子句WHERE位= 0.这样,该索引将只存储1%的行。 然后做一个WHERE位= 0将简单地让查询优化器select该索引,并从它的所有行将位= 0.您还有一个好处是有一个非常小的磁盘空间需要比较该位上的完整索引。

就其本身而言,不会导致非常小的select性。 作为复合索引的一部分。 相当可能,但只有在其他平等的列之后。

如果你想知道一个指数是否有你想要的效果: 再次testing和testing。

一般来说,你不需要一个索引不足以缩小你的表,因为维护一个索引的成本。 (成本>利润)。 但是,如果你的情况下的指数会削减一半,你可能会得到一些东西,但把它放在桌子上。 这一切都取决于你的表的确切大小/结构以及你如何使用它(读/写次数)。

SQL Server 2000中的位字段不能编入索引,正如当时的联机丛书中所述:

整数数据types1,0或NULL。

备注

types位的列不能有索引。

是的,如果你只有几行,数以百万计,索引将有所帮助。 但是如果你想在这种情况下做到这一点,你需要使列成为一个tinyint

注意 :企业pipe理器不会让您在位列上创build索引。 如果你希望你仍然可以手动创build一个位列的索引:

 CREATE INDEX IX_Users_IsActiveUsername ON Users ( IsActive, Username ) 

但是SQL Server 2000实际上不会使用这样一个索引 – 运行一个查询索引将是一个完美的候选人,例如:

 SELECT TOP 1 Username FROM Users WHERE IsActive = 0 

SQL Server 2000将执行表扫描,而不是索引甚至不存在。 如果将列更改为tinyint,则SQL Server 2000 执行索引查找。 另外,下面的非覆盖查询:

 SELECT TOP 1 * FROM Users WHERE IsActive = 0 

它将执行索引查找,然后是书签查找。


SQL Server 2005确实对位列上的索引提供了有限的支持。 例如:

 SELECT TOP 1 Username FROM Users WHERE IsActive = 0 

将通过覆盖指数引起索引寻找。 但没有涵盖的情况下:

 SELECT TOP 1 * FROM Users WHERE IsActive = 0 

不会导致索引查找,然后是书签查找,它将执行表扫描(或聚簇索引扫描),而不是执行索引查找,然后是书签查找。

通过实验和直接观察validation。

很晚回答…

是的, 根据SQL CAT团队 (更新,已合并)

这是一个常见的查询? 在寻找“less数”logging时可能是值得的,但对其他行却无济于事。 还有其他方法来识别数据?

基数是一个因素,另一个是指数如何划分您的数据。 如果你有大概一半半的时间,那么这将有所帮助。 (假设该指数比其他指数更好select)。 但是,您经常插入和更新? 为SELECT性能添加索引也会影响INSERT,UPDATE和DELETE性能,所以请记住这一点。

我会说,如果1s到0s(反之亦然)不是好于75%到25%,不要麻烦。

测量前后的响应时间,看看是否值得; 理论上它应该提高使用索引字段的查询的性能,但是这实际上取决于true / false值的分布以及涉及您关心的查询的其他字段

伊恩·博伊德(Ian Boyd)说,你不能通过企业pipe理器(SQL Server 2000)来实现这一点(请参阅他关于通过T-SQL创build它的说明。

你需要在这里聪明地查询,你必须知道你的列的负载值,如果你的系统中的真实负载更多,并且你想检查所有真正的值写你的查询来检查不错误..这将帮助很多,它只是欺骗。