我记得在某一点上读取索引具有低基数(少量不同值)的字段并不值得做。我承认我对索引如何理解为什么不了解。
那么,如果我有一个包含1亿行的表,并且我选择位字段为1的记录呢?让我们说在任何时间点,只有少数记录位字段为1(而不是0)。是否值得索引该位字段?为什么呢?
当然我可以测试它并检查执行计划,我会这样做,但我也很好奇它背后的理论。什么时候基数很重要,什么时候不重要?
答案 0 :(得分:66)
考虑SQL中的索引是什么 - 索引实际上是指向其他内存块(即指向行的指针)的内存块。索引被分解为页面,以便可以根据使用情况从内存中加载和卸载索引的某些部分。
当你要求一组行时,SQL使用索引比表扫描更快地查找行(查看每一行)。
SQL具有聚簇索引和非聚簇索引。我对聚簇索引的理解是它们将类似的索引值分组到同一页面中。这样,当您要求所有与索引值匹配的行时,SQL可以从内存的聚簇页面返回这些行。这就是为什么尝试集群索引GUID列是一个坏主意 - 您不要尝试聚集随机值。
索引整数列时,SQL的索引包含每个索引值的一组行。如果你的范围是1到10,那么你将有10个索引指针。根据有多少行,可以不同地分页。如果您的查询查找匹配“1”的索引,然后查找Name包含“Fred”的位置(假设Name列未编入索引),则SQL会非常快速地获取与“1”匹配的行集,然后通过表扫描查找其余行。
那么SQL真正在做的是尝试减少必须迭代的工作集(行数)。
索引位字段(或某个窄范围)时,只会将工作集减少与该值匹配的行数。如果你有少量的行匹配它会减少你的工作集很多。对于50/50分布的大量行,与保持索引最新相比,它可能会为您带来非常小的性能提升。
每个人都说测试的原因是因为SQL包含一个非常聪明且复杂的优化器,如果它决定表扫描速度更快,或者可能使用排序,或者可能组织内存页面,它可能会忽略索引,但它很喜欢。
答案 1 :(得分:16)
我刚刚通过另一个问题遇到了这个问题。假设您的声明只有少数记录假定值为1(并且那些是您感兴趣的那些),那么过滤后的索引可能是一个不错的选择。类似的东西:
create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1
这将创建一个小得多的索引,优化器足够聪明,当它是查询中的谓词时就可以使用。
答案 2 :(得分:9)
1亿条记录,只有少数记录的位字段设置为1?是的,我认为索引位字段肯定会加快查询bit = 1记录。您应该从索引获取对数搜索时间,然后只触摸bit = 1记录的几个页面。否则,您必须触摸1亿记录表的所有页面。
然后,我绝对不是数据库专家,可能会遗漏一些重要的东西。
答案 3 :(得分:7)
虽然我不认为我会单独索引JUST一个列,但将列列作为复合索引的一部分是很常见的。
一个简单的例子是当你的应用程序几乎总是在寻找活跃的客户时,ACTIVE,LASTNAME而不是lastname上的索引。
答案 4 :(得分:7)
如果你的发行版是众所周知且不平衡的,比如99%的行是bit = 1而1%是bit = 0,当你使用bit = 1执行WHERE子句时,全表扫描将围绕与索引扫描同时进行。如果你想快速查询bit = 0,我知道的最好方法是创建一个过滤索引,添加一个子句WHERE bit = 0.这样,该索引只存储1%的行。然后执行WHERE位= 0将简单地让查询优化器选择该索引,并且其中的所有行都将为bit = 0.还有一个好处是比较位上的完整索引需要非常少量的磁盘空间
答案 5 :(得分:7)
如果您还没有阅读,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的新文章讨论了这个主题:
答案 6 :(得分:2)
如果您想知道索引是否具有您想要的效果:再次测试并测试。
一般情况下,由于维护索引的成本,您不希望索引不足以缩小表。 (成本>利润)。但是如果您的情况下的索引会将表格减少一半,那么您可能会获得一些东西,但却将其放在桌面上。这完全取决于表的确切大小/结构以及如何使用它(读/写次数)。
答案 7 :(得分:2)
当然值得,特别是如果您需要按该值检索数据。它类似于使用稀疏矩阵而不是使用普通矩阵。
现在使用SQL 2008,您可以使用分区功能,并且您可以过滤索引中的数据。早期版本的缺点是会为所有数据制作索引,但可以通过将有趣的值存储在单独的文件组中来优化。
答案 8 :(得分:2)
正如其他人所说,你会想要衡量这一点。我不记得我在哪里读过这篇文章,但是为了使索引有效,列需要具有非常高的基数(大约95%)。对此最好的测试是构建索引并检查BIT字段的0和1值的执行计划。如果您在执行计划中看到索引查找操作,那么您就知道将使用您的索引。
您最好的做法是使用基本的SELECT * FROM表来测试WHERE BitField = 1;查询并逐步从中逐步构建功能,直到您对应用程序有一个真实的查询,检查每个步骤的执行计划,以确保仍然使用索引查找。无可否认,不能保证这个执行计划将用于生产,但很有可能会出现。
有些信息可以在sql-server-performance.com forums和引用的article
中找到答案 9 :(得分:2)
“我记得在某一点上读取索引具有低基数的字段(少量不同的值)并不值得做”
这是因为SQL Server几乎总能找到更有效的方法来执行表扫描而不是读取索引。所以基本上你的索引永远不会被使用,维护它是一种浪费。正如其他人所说,复合指数可能没问题。
答案 10 :(得分:2)
如果您的目标是查询位字段值等于'1'的记录,您可以尝试基表的索引视图,该视图仅包含位字段等于'1'的记录。在企业版中,如果查询可以使用索引视图而不是指定的表来提高查询性能,那么它将使用该视图。理论上,这将增加选择查询的速度,这些查询仅查找位字段值为“1”的记录。
http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx
所有这些都假定您是Microsoft SQL Server 2005 Enterprise。同样可能适用于2008年,我不熟悉该版本。
答案 11 :(得分:1)
您无法索引SQL Server 2000中的位字段,如当时的联机丛书中所示:
位
整数数据类型1,0或NULL。
备注
位类型的列不能 有索引。
是的,如果您只有少数行,数百万,索引将有所帮助。但是如果你想在这种情况下这样做,你需要将列设为tinyint
。
注意:企业管理器不允许您在位列上创建索引。如果您希望仍然可以在位列上手动创建索引:
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
不会导致索引查找后跟书签查找,它将执行表扫描(或聚簇索引扫描),而不是执行索引查找,然后执行书签查找。
通过实验和直接观察验证。
答案 12 :(得分:1)
单凭它,因为它导致选择性很小。作为复合指数的一部分。很可能,但只能在其他平等列之后。
答案 13 :(得分:1)
很晚才回答......
是的,它可以是useful according to SQL CAT team(已更新,已合并)
答案 14 :(得分:0)
你需要在这里聪明地查询,你必须知道你的列上的负载值,如果你的系统中的负载更多,并且你想要检查写入查询的所有真值以检查不是假的..它将帮助很多,它只是欺骗。
答案 15 :(得分:0)
衡量之前和之后的响应时间,看看是否值得;从理论上讲,它应该提高使用索引字段的查询的性能,但它实际上取决于真/假值的分布以及您关注的查询中涉及的其他字段
答案 16 :(得分:0)
Ian Boyd说他不能通过Enterprise Manager for SQL 2000来做这件事(请参阅他关于通过T-SQL创建它的说明。
答案 17 :(得分:0)
基数是一个因素,另一个因素是指数对数据的划分程度。如果你有大约一半和一半,那么它会有所帮助。 (假设该索引是比其他索引更好的选择路径)。但是,您多久插入和更新一次?为SELECT性能添加索引也会损害INSERT,UPDATE和DELETE性能,因此请记住这一点。
我想说,如果1s到0s(反之亦然)不超过75%到25%,请不要打扰。
答案 18 :(得分:0)
这是一个常见的查询吗?在寻找“少数”记录时可能是值得的,但对其他行不会有太多帮助。还有其他方法来识别数据吗?