我应该在SQL Server中索引位字段吗?


99

我记得曾经读过一点,对基数较低(不同值的数量很少)的字段进行索引并不是真正值得做的事情。我承认我对索引的工作方式了解不足,无法理解为什么会这样。

那么,如果我有一个包含1亿行的表,而我选择的是位字段为1的记录怎么办?假设在任何时间点,只有少数记录的位字段为1(而不是0)。是否值得索引该位字段?为什么?

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


这是常见查询吗?寻找“少量”记录可能很值得,但在其他行上却无济于事。还有其他识别数据的方法吗?
杰森·索尔多(Jason Saldo)

4
虽然我认为我自己不会仅索引一个位列,但将位列作为复合索引的一部分包括在内是很常见的。一个简单的例子是当您的应用程序几乎总是在寻找活跃客户时,在ACTIVE,LASTNAME上的索引而不是姓氏。
BradC

“我记得曾经读过一点,对基数低(很少数量的不同值)建立索引的索引确实不值得”指数。因此,基本上您的索引将永远不会被使用,并且维护索引是一种浪费。正如其他人所说,在复合索引中可能没问题。
DJ。

5
我不同意。如果您的分布是50/50,则您将永远不会使用索引,因为这样做可以更快地执行表扫描。但是,如果您只有5个,1个值和1百万个0值,则很可能在搜索1时使用索引
。– Kibbee,

1
在您提供的示例中,我更倾向于将姓氏放在首位。它取决于特定的查询工作量,但通常首先具有更具选择性的列,这意味着更可能使用索引。
米奇小麦

Answers:


72

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

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

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

当您为整数列建立索引时,SQL的索引包含每个索引值的一组行。如果范围为1到10,则将有10个索引指针。根据有多少行,可以不同地分页。如果查询查找与“ 1”匹配的索引,然后“名称”包含“ Fred”(假设“名称”列未建立索引),则SQL会非常快速地获取与“ 1”匹配的行集,然后通过表扫描找到其余的行。

因此,SQL真正在做的是尝试减少必须迭代的工作集(行数)。

当您为一个位字段(或某个狭窄范围)建立索引时,只将工作集减少与该值匹配的行数。如果匹配的行数很少,则会大大减少您的工作集。对于具有50/50分布的大量行,与保持索引为最新状态相比,它可能不会给您带来多少性能提升。

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


因此,这听起来像是如果我只有少数几行中的位字段为1(例如,跟踪“ IsProcessed”),那么索引会很好,因为它将按值对它们进行排序,然后可以选择小工作集很快。如果您同意,请添加该内容,我会接受。
jeremcc

2
在上一条评论中,我的意思是这样的说法:“如果将位字段(或某个狭窄范围)编入索引,则只能将工作集减半”,如果分配偏重于一个值,则是不正确的。但我喜欢其余的答案,因此,如果您解决该问题,我会接受。
jeremcc

1
做完了 我当时想在一百万行中,一个位字段将具有50%的分布,但是您是正确的,对于特定的问题空间,它可以大大减少工作集。
Geoff Cox

值得一看的是,有索引和无索引的执行计划,看看是否正在使用索引,以及它是否实际上降低了查询成本。简单而科学!
onupdatecascade 2010年

索引位字段+另一个字段怎么办?例如。在Web活动日志中,一个将为时间戳编制索引,但另一个有用的索引可能位于“ IsHTTPS” +时间戳的位字段上,以快速查看所有https操作。效率低下吗?
ingredient_15939

19

我只是通过另一个方式遇到了这个问题。假设您的声明中只有少数记录假定值为1(并且这些记录是您感兴趣的值),那么筛选索引可能是一个不错的选择。就像是:

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

这将创建一个较小的索引,当优化程序在查询中作为谓词时,该优化程序将足够聪明地使用。


1
值得注意的是,查询中的谓词必须硬编码为过滤索引中的值。如果您在参数中传递值yourBitColumn = @value,那么优化器将无法确定过滤后的索引是否可用。
geofftnz

2
有很多解决方法,但是您是对的。优化器需要在编译时保证与过滤后的索引谓词匹配的所有谓词的值都是静态的/不变的,因为创建适用于任何参数集的通用计划是优化器的工作。
本图尔

9

1亿条记录,只有少数将位字段设置为1?是的,我认为索引位字段肯定会加快查询bit = 1记录的速度。您应该从索引中获取对数搜索时间,然后仅触摸位= 1的几页记录。否则,您必须触摸1亿条记录表的所有页面。

再说一次,我绝对不是数据库专家,并且可能会缺少一些重要的东西。


8

如果您的分布是众所周知的并且不平衡,例如99%的行是bit = 1且1%的行是bit = 0,则当您使用bit = 1进行WHERE子句时,全表扫描与索引扫描。如果要在bit = 0的地方进行快速查询,我所知道的最好方法是创建一个过滤索引,在其中添加一个WHERE bit = 0子句。那样,该索引将只存储1%的行。然后执行WHERE bit = 0将仅使查询优化器选择该索引,并且该索引中的所有行都将为bit =0。与该位上的完整索引进行比较,您还具有非常少量的磁盘空间的好处。 。


2
如果99%的行的bit = 1,则优化器应忽略索引并进行表扫描。使用索引实际上比表扫描更糟糕,至少在旋转驱动器上,更多I / O和从磁盘的非连续读取方面更是如此。过滤后的索引(相当于Postgres:部分索引)是解决之道。我想是因为问题已经提出多年了,所以这个答案没有得到应有的投票。
安德鲁·拉撒路

7

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

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


7
在您提供的示例中,我更倾向于将姓氏放在首位。它取决于特定的查询工作量,但通常首先具有更具选择性的列,这意味着更可能使用索引。
米奇小麦

7

此文章不再可见
Homer6 2011年

@ Homer6我在本文的新首页添加了一个链接。
杰夫,

新链接进入Toad World主页。
N西

使用Wayback机器找到了该文章,并找到了新的相关文章。希望这可以帮助。
杰夫2014年

2

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

现在,使用SQL 2008,您可以使用分区功能,并且能够过滤索引中的数据。早期版本的缺点是将为所有数据创建索引,但是可以通过将有趣的值存储在单独的文件组中来优化索引。


2

正如其他人所说,您将需要进行度量。我不记得在哪儿读过这篇文章,但是一列需要具有非常高的基数(大约95%)才能使索引有效。最好的测试方法是建立索引并检查BIT字段的0和1值的执行计划。如果在执行计划中看到索引查找操作,则说明将使用索引。

最好的做法是使用基本的SELECT * FROM表WHERE BitField = 1来测试;查询并逐步从那里逐步构建功能,直到对您的应用程序提出切合实际的查询为止,并仔细检查执行计划,以确保仍在使用索引查找。诚然,不能保证此执行计划将在生产中使用,但是很有可能会使用它。

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


重要的不是整个列的基数。它是WHERE子句的选择性。因此,如果很少有值为1的列,则对索引还是不错的。如果是50/50(例如,男性/女性),那么就不值得了。
WW。

2

“我记得曾经读过一点,索引基数低(不同值的数量少)的字段确实不值得做”

那是因为SQL Server几乎总是发现仅进行表扫描比读取索引更有效。因此,基本上您的索引将永远不会被使用,并且维护它是一种浪费。正如其他人所说,在复合索引中可能没问题。


2

如果您的目标是使查询查询位字段值等于“ 1”的记录更快,则可以尝试使用基表的索引视图,该索引表仅包含位字段值等于“ 1”的记录。在企业版中,如果查询可以使用索引视图而不是指定的表来提高查询性能,则它将使用该视图。从理论上讲,这将提高仅查找位字段值为“ 1”的记录的选择查询的速度。

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

所有这些都假定您是Microsoft SQL Server 2005 Enterprise。这可能适用于2008,但我对该版本不熟悉。


2

如果您想知道索引是否具有您想要的效果:请再次测试。

通常,由于维护索引的成本,您不希望索引不能充分缩小表的范围。(成本>利润)。但是,如果您的情况下的索引会将表切成两半,则除了将其放在表上之外,您可能会获得一些好处。所有这些都取决于表的确切大小/结构以及如何使用它(读/写数)。


1

就其本身而言,没有,因为它导致极小的选择性。作为复合索引的一部分。很有可能但仅在其他相等列之后。


1

不能像在联机丛书中指出的那样在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

不会导致在索引查找之后进行书签查找,而是将执行表扫描(或群集索引扫描),而不是执行索引查找后进行书签查找。

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


仅供参考-SQL Server 2005 Management Studio确实允许您这样做。
jeremcc

我的SQL Server 2000副本使我可以在bit列上设置索引。
Kibbee

我的SQL Server 2000副本不允许我在bit列上设置索引。
伊恩·博伊德


0

这是常见查询吗?寻找“少量”记录可能很值得,但在其他行上却无济于事。还有其他识别数据的方法吗?


0

基数是一个因素,另一个是索引对数据的划分程度。如果您有大约一半的1s和一半的0s,那么它将有所帮助。(假定该索引比其他某些索引更适合选择)。但是,您多久插入和更新一次?为SELECT性能添加索引还会损害INSERT,UPDATE和DELETE性能,因此请记住这一点。

我会说,如果1到0(反之亦然)不超过75%到25%,请不要打扰。


1
我不同意。如果您的分布是50/50,则您将永远不会使用索引,因为这样做可以更快地执行表扫描。但是,如果您只有5个,1个值和1百万个0值,则很可能在搜索1时使用索引
。– Kibbee,

0

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


0

伊恩·博伊德(Ian Boyd)说您无法通过Enterprise Manager for SQL 2000做到这一点时是正确的(请参阅有关通过T-SQL创建它的说明。


0

您需要在这里聪明地进行查询,如果系统中的true负载更多,则必须知道列上的负载值,并且您想检查所有true值以编写查询以检查是否为false。 ,这只是把戏。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.