我记得曾经读过一点,对基数较低(不同值的数量很少)的字段进行索引并不是真正值得做的事情。我承认我对索引的工作方式了解不足,无法理解为什么会这样。
那么,如果我有一个包含1亿行的表,而我选择的是位字段为1的记录怎么办?假设在任何时间点,只有少数记录的位字段为1(而不是0)。是否值得索引该位字段?为什么?
当然,我可以测试它并检查执行计划,并且我会这样做,但是我也对它背后的理论感到好奇。基数什么时候重要,什么时候不重要?
我记得曾经读过一点,对基数较低(不同值的数量很少)的字段进行索引并不是真正值得做的事情。我承认我对索引的工作方式了解不足,无法理解为什么会这样。
那么,如果我有一个包含1亿行的表,而我选择的是位字段为1的记录怎么办?假设在任何时间点,只有少数记录的位字段为1(而不是0)。是否值得索引该位字段?为什么?
当然,我可以测试它并检查执行计划,并且我会这样做,但是我也对它背后的理论感到好奇。基数什么时候重要,什么时候不重要?
Answers:
考虑一下SQL中的索引是什么-索引实际上是指向其他内存块(即,指向行的指针)的内存块。索引分为多个页面,以便可以根据使用情况从内存中加载和卸载索引的某些部分。
当您请求一组行时,SQL使用索引比表扫描(查看每一行)更快地找到行。
SQL具有群集索引和非群集索引。我对聚集索引的理解是,它们将相似的索引值分组到同一页面中。这样,当您要求所有与索引值匹配的行时,SQL可以从内存的群集页面返回这些行。这就是为什么尝试对GUID列建立索引是一个坏主意的原因-您不要尝试对随机值进行聚类。
当您为整数列建立索引时,SQL的索引包含每个索引值的一组行。如果范围为1到10,则将有10个索引指针。根据有多少行,可以不同地分页。如果查询查找与“ 1”匹配的索引,然后“名称”包含“ Fred”(假设“名称”列未建立索引),则SQL会非常快速地获取与“ 1”匹配的行集,然后通过表扫描找到其余的行。
因此,SQL真正在做的是尝试减少必须迭代的工作集(行数)。
当您为一个位字段(或某个狭窄范围)建立索引时,只将工作集减少与该值匹配的行数。如果匹配的行数很少,则会大大减少您的工作集。对于具有50/50分布的大量行,与保持索引为最新状态相比,它可能不会给您带来多少性能提升。
每个人都说要测试的原因是因为SQL包含一个非常聪明和复杂的优化器,如果它决定表扫描速度更快,或者可以使用排序,或者可以组织内存页,那么它可能会忽略索引。
我只是通过另一个方式遇到了这个问题。假设您的声明中只有少数记录假定值为1(并且这些记录是您感兴趣的值),那么筛选索引可能是一个不错的选择。就像是:
create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1
这将创建一个较小的索引,当优化程序在查询中作为谓词时,该优化程序将足够聪明地使用。
yourBitColumn = @value
,那么优化器将无法确定过滤后的索引是否可用。
如果您的分布是众所周知的并且不平衡,例如99%的行是bit = 1且1%的行是bit = 0,则当您使用bit = 1进行WHERE子句时,全表扫描与索引扫描。如果要在bit = 0的地方进行快速查询,我所知道的最好方法是创建一个过滤索引,在其中添加一个WHERE bit = 0子句。那样,该索引将只存储1%的行。然后执行WHERE bit = 0将仅使查询优化器选择该索引,并且该索引中的所有行都将为bit =0。与该位上的完整索引进行比较,您还具有非常少量的磁盘空间的好处。 。
如果您还没有阅读它,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的新文章讨论了这个主题:
Wayback机器:http ://web.archive.org/web/20150508115802/http: //www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an一个位列索引将永远不会被使用.aspx
正如其他人所说,您将需要进行度量。我不记得在哪儿读过这篇文章,但是一列需要具有非常高的基数(大约95%)才能使索引有效。最好的测试方法是建立索引并检查BIT字段的0和1值的执行计划。如果在执行计划中看到索引查找操作,则说明将使用索引。
最好的做法是使用基本的SELECT * FROM表WHERE BitField = 1来测试;查询并逐步从那里逐步构建功能,直到对您的应用程序提出切合实际的查询为止,并仔细检查执行计划,以确保仍在使用索引查找。诚然,不能保证此执行计划将在生产中使用,但是很有可能会使用它。
可以在sql-server-performance.com论坛和参考文章中找到一些信息。
如果您的目标是使查询查询位字段值等于“ 1”的记录更快,则可以尝试使用基表的索引视图,该索引表仅包含位字段值等于“ 1”的记录。在企业版中,如果查询可以使用索引视图而不是指定的表来提高查询性能,则它将使用该视图。从理论上讲,这将提高仅查找位字段值为“ 1”的记录的选择查询的速度。
http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx
所有这些都假定您是Microsoft SQL Server 2005 Enterprise。这可能适用于2008,但我对该版本不熟悉。
您不能像在联机丛书中指出的那样在SQL Server 2000中对位字段建立索引:
一点
整数数据类型1、0或NULL。
备注
bit类型的列不能在其上具有索引。
是的,如果只有几行(百万行),那么索引将有所帮助。但是,如果要在这种情况下执行此操作,则需要将列设置为a tinyint
。
注意:企业管理器不允许您在bit列上创建索引。如果愿意,您仍然可以在bit列上手动创建索引:
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
不会导致在索引查找之后进行书签查找,而是将执行表扫描(或群集索引扫描),而不是执行索引查找后进行书签查找。
通过实验和直接观察验证。
基数是一个因素,另一个是索引对数据的划分程度。如果您有大约一半的1s和一半的0s,那么它将有所帮助。(假定该索引比其他某些索引更适合选择)。但是,您多久插入和更新一次?为SELECT性能添加索引还会损害INSERT,UPDATE和DELETE性能,因此请记住这一点。
我会说,如果1到0(反之亦然)不超过75%到25%,请不要打扰。
您需要在这里聪明地进行查询,如果系统中的true负载更多,则必须知道列上的负载值,并且您想检查所有true值以编写查询以检查是否为false。 ,这只是把戏。