我是否应该索引 SQLServer 中的位字段?

我记得曾经读到过,为一个基数较低(不同值的数量较少)的字段建立索引实际上并不值得。我承认,我对索引的工作原理了解得不够,无法理解其中的原因。

So what if I have a table with 100 million rows in it, and I am selecting records where a bit field is 1? And let's say that at any point in time, there are only a handful of records where the bit field is 1 (as opposed to 0). Is it worth indexing that bit field or not? Why?

当然,我可以仅仅测试它并检查执行计划,我将这样做,但是我也对其背后的理论感到好奇。基数什么时候重要,什么时候不重要?

42317 次浏览

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

这是一个常见的问题吗?在查找“屈指可数的”记录时,这可能是值得的,但对其他行没有太大帮助。还有其他方法来识别数据吗?

如果你想知道一个索引是否有你想要的效果: 一遍又一遍地测试。

通常,由于维护索引的成本,您不希望索引没有足够地缩小表的范围。(成本 > 利润)。但是,如果在您的情况下,索引将把表切成两半,您可能会得到一些东西,但把它放在桌子上。这完全取决于表的确切大小/结构以及如何使用它(读/写的次数)。

当然值得,特别是如果您需要根据该值检索数据。这将类似于使用稀疏矩阵,而不是使用法线矩阵。

现在使用 SQL2008,您可以使用分区函数,并且能够过滤索引中的数据。早期版本的缺点是索引将针对所有数据,但是可以通过将感兴趣的值存储在单独的文件组中进行优化。

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

最好的方法是使用基本的 SELECT * FROM 表 WHERE BitField = 1进行测试; 查询并从那里一步一步地构建功能,直到你对应用程序有一个实际的查询,每一步都检查执行计划,以确保仍然使用索引查找。无可否认,并不能保证这个执行计划将在生产中使用,但是很有可能会使用。

一些信息可以在 Sql-server-performance.com 论坛和引用的 文章中找到

虽然我不认为我会索引只是一个位列本身,它是非常常见的包括位列作为一个复合索引的一部分。

一个简单的例子就是在您的应用程序几乎总是在寻找活跃的客户时,Aactive,LASTNAME 上的索引而不仅仅是 LASTNAME。

"I remember reading at one point that indexing a field with low cardinality (a low number of distinct values) is not really worth doing"

这是因为 SQLServer 几乎总是发现仅进行表扫描比读取索引更有效。所以基本上你的索引永远不会被使用,维护它是一种浪费。正如其他人所说,在复合指数中,这可能是可以接受的。

基数是一个因素,另一个因素是索引如何划分你的数据。如果你有一半1和一半0,那么它将有所帮助。(假设该索引比其他索引更适合选择)。但是,插入和更新的频率是多少?为 SELECT 性能添加索引也会损害 INSERT、 UPDATE 和 DELETE 性能,因此请记住这一点。

I would say, if the 1s to 0s (or vice versa) isn't better than 75% to 25%, don't bother.

You 不行 index a bit field in SQL Server 2000, as was indicated in the Books Online at the time:

整数数据类型1、0或 NULL。

备注

类型的列不能 上面有索引。

Yes, if you have only a handful of rows, out of millions, an index will help. But if you want to do it in this case you need to make the column a tinyint.

注意 : Enterprise Manager 不允许您在位列上创建索引。如果您希望仍然可以手动创建位列的索引:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
IsActive,
Username
)

但是 SQLServer2000实际上不会使用这样的索引——运行一个查询,其中的索引将是一个完美的候选索引,例如:

SELECT TOP 1 Username
FROM Users
WHERE IsActive = 0

SQL Server 2000 will do a table scan instead, acting as though the index doesn't even exist. If you change the column to a tinyint SQL Server 2000 威尔 do an index seek. Also, the following non-covered query:

SELECT TOP 1 *
FROM Users
WHERE IsActive = 0

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


SQLServer2005对位列上的索引的支持有限,例如:

SELECT TOP 1 Username
FROM Users
WHERE IsActive = 0

将会导致一个覆盖指数的索引寻找,但未覆盖的情况:

SELECT TOP 1 *
FROM Users
WHERE IsActive = 0

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

通过实验和直接观察验证。

就其本身而言,没有,因为它几乎没有什么选择性。作为复合索引的一部分。很有可能,但只有在其他等式列之后。

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

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

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

当索引一个整数列时,SQL 的索引包含每个索引值的一组行。如果范围是1到10,那么就有10个索引指针。根据行数的不同,可以对此进行不同的分页。如果您的查询查找匹配“1”的索引,然后在 Name 包含“ Fred”的地方(假设 Name 列没有被索引) ,SQL 会很快得到匹配“1”的一组行,然后进行表扫描以找到其余的行。

因此,SQL 实际上是在试图减少它必须迭代的工作集(行数)。

当索引一个位字段(或某个较窄的范围)时,只需减少与该值匹配的行数即可。如果匹配的行数很少,那么工作集就会大大减少。对于具有50/50分布的大量行,与保持索引最新相比,它可能只能为您带来很少的性能提升。

每个人之所以说要测试,是因为 SQL 包含一个非常聪明和复杂的优化器,如果它认为表扫描更快,或者可能使用排序,或者可能按照它非常喜欢的方式组织内存页面,它可能会忽略索引。

100-million records with only a few having the bit field set to 1? Yes, I would think indexing the bit field would definitely speed up querying the bit=1 records. You should get logarithmic search time from the index and then only touch the few pages with bit=1 records. Otherwise, you'd have to touch all pages of the 100-million record table.

不过话说回来,我绝对不是数据库专家,可能遗漏了什么重要的东西。

If your goal is to make querying for records where the bit field value equals '1' faster you might try an indexed view of your base table which only contains records where your bit field equals '1'. In enterprise edition if a query could make use of an indexed view instead of a specified table to improve query performance it will use the view. In theory this would increase the speed of select queries which only look for records with a bit field value of '1'.

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

所有这些都假设你是2005年的企业 Microsoft SQL Server。2008年可能也是如此,我对那个版本并不熟悉。

Ian Boyd 说你不能通过 SQL 2000的企业管理器来实现这一点是正确的(参见他关于通过 T-SQL 创建它的说明)。

很晚的回答。

是的,它可以是 有用的根据 SQL CAT 团队(更新,已经合并)

我刚刚从另一个问题中偶然发现了这个问题。假设您的语句中只有少数记录假设值为1(并且这些记录是您感兴趣的记录) ,那么过滤索引可能是一个不错的选择。比如:

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

这将创建一个非常小的索引,当这个索引是查询中的谓词时,优化器可以使用这个索引。

If your distribution is pretty known and unbalanced, like 99% of the rows are bit = 1 and the 1% are bit = 0, when you do a WHERE clause with bit = 1, a full table scan will be around the same time as the index scan. If you want to have a fast query where bit = 0, the best way I know is create a filtered index, adding a clause WHERE bit = 0. That way, that index will only store the 1% row. Then doing a WHERE bit = 0 will simply let the query optimizer choose that index, and all rows from it will be bit = 0. You also have the benefit to have a very small amount of disk space required compare a full index on the bit.

你需要聪明的在这里查询,你必须知道你的列负载值,如果你的系统负载更多的是真,你想检查所有的真值写你的查询检查不是假。.它会帮助很多,它只是欺骗。