2011-08-19 86 views
6

我们目前有一种情况,一个表有效地具有几个(10至15)布尔标志(不可为空的bit字段)。不幸的是,在逻辑层面上将其过分简化是不太可能的,因为任何布尔值的组合都是允许的。SQL Server中多位字段的索引

有问题的表是一个事务表,最终可能有数千万行,并且插入和选择性能都相当重要。尽管目前我们对数据的分布还不太确定,但所有标志的组合都应该提供相对良好的基数,即使它成为SQL Server使用的“值得”索引。

典型的选择查询场景可能是仅基于3或4个标记来选择记录,例如, WHERE FLAG3=1 AND FLAG7=0 AND FLAG9=1。为这些select查询使用的所有标志组合创建单独的索引并不实际,因为它们中会有很多。

鉴于这种情况,有效索引这些字段的建议方法是什么?该表是新的,所以现在还没有需要担心的数据,并且我们在表的实际实施中具有相当大的灵活性。

有迹象表明,我们正在考虑在目前两个主要选项:

  • 创建一个单一的指标,其中包括所有的位字段(这可能会包括1个或2个其他int领域它总是使用)。我担心的是,鉴于仅包括几个字段的典型用法,这种方法会跳过索引并求助于表扫描。我们称之为选项A(阅读了一些回复后,似乎这种方法效果不好,因为索引中字段的顺序会产生差异,从而无法在所有字段上有效地进行索引)。
  • 有效地做我认为SQL Server在内部完成的任务,并使用二元运算符(将数字与1和2,4,8等组合在一起)将位字段编码为单个int字段。我的关注点是我们需要做一些计算来查询这个编码字段,这会再次跳过索引。维护和解决方案的复杂性也是一个问题。我们称之为选项B附加信息:参数对于这种方法是我们可以有一个相对简单和短的索引,其中包括表和该字段中的一个或两个其他字段。其他字段将缩小需要评估的记录数量,并且由于编码字段将包含我们所有的位字段,因此SQL Server将能够使用从索引直接检索的数据执行计算(即索引扫描)而不是表(即表格扫描)。

目前,我们非常倾向于选项B。为了完整起见,这将在SQL Server 2008上运行。

任何意见将不胜感激。

编辑:拼写,清晰度,查询示例,关于的附加信息选项B

回答

3

虽然有可能的方式来解决对您现有的表架构你的索引的问题,我想这减少到正常化问题:

例如,我会强烈建议创造了一系列的新表:该位标志的名称

  1. 查找表。例如CREATE TABLE Flags (id int IDENTITY(1,1), Name varchar(256))(如果您想手动控制ID,例如2,4,8,16,32,64,128作为二进制标记,则不必为身份验证种子列创建身份验证列)。
  2. 创建新的link-表,其中包含原始数据表的id和新的链接表,例如CREATE TABLE DataFlags_Link (id int IDENTITY(1,1), MyFlagId int, DataId int)

然后,您可以在DataFlags_Link表创建索引,并写这样的查询:

SELECT Data.* 
FROM Data 
INNER JOIN DataFlags_Link ON Data.id = DataFlags_Link.DataId 
WHERE DataFlags_Link.MyFlagId IN (4,7,2,8) 

至于性能,这就是好的DBA维护进来你要设置索引填充。 - 因素和填充适当的表,并运行定期索引碎片整理或按计划重建索引。

性能和维护与数据库齐头并进。没有其他人就没有一个。

+1

有趣的是,感谢您的输入。这种方法确实有一些缺点,例如记录将被“复制”为每个DataFlags_Link记录(并且我不确定是否会导致重大性能下降)。另外,我们的查询通常会检查标志是否为0;即不存在于您的链接表中(对不起,如果我没有在问题中指定这个)。我想,它最终会变得非常混乱。 –

+0

@Dnail:flag = 0检查类似于'... WHERE NOT EXISTS(SELECT * FROM DataFlags_Link dfl WHERE dfl.DataId = Data.id)'并且应该使用索引。 –

6

单个BIT列通常不够具有足够的选择性,甚至不能考虑用于索引。因此,单个BIT列上的索引实际上没有意义 - 平均而言,您始终必须搜索表中大约一半的条目(50%选择性),因此SQL Server查询优化器将使用表扫描。

如果您在所有15个bit列上创建单个索引,那么您不会遇到这个问题 - 因为您有15个是/否选项,您的索引将变得非常有选择性。

问题是:位列的序列很重要。如果您的SQL语句至少使用最左侧的BIT列的1-n,那么只会考虑您的索引

所以,如果你的指数是上

Col1,Col2,Col3,....,Col14,Col15 

那么它可能被用于使用

  • Col1
  • Col1Col2
  • Col1Col2Col3 查询。 ...

等等。但它不能用于指定Col6,Col9Col14的查询。

因此,我并不认为您的BIT列的索引真的有很大意义。

那些15 BIT列是您用于查询的唯一列吗?如果不是的话,我会尝试将那些最常用的BIT列与其他列进行合并,例如对NameCol7什么索引(然后你BIT列可以添加一些额外的选择性另一个索引)

+1

感谢列顺序信息(我实际上已经忘记了),我们将尝试提出一个排序可能会工作。关于与其他栏目的结合;是的,我们总是会添加一列到应该立即过滤95%的表的索引。出于好奇,关于“50%选择性”部分 - 如果这个数字偏向98%,而你正在查询2%,会发生什么? SQL Server是否足够聪明以利用它?我确实意识到在这种情况下分区可能要优越得多。 –

+1

@Daniel B:是的,SQL Server的查询优化器会保存您的数据及其分布的统计信息。如果一个索引确实有足够的选择性来保证它的使用,那么SQL Server的查询优化器将使用它。所以,如果你有这样一个“倾斜”的列 - 是的,该列的索引将有助于在你选择1-2%的情况下 - 不在另一种情况下,但:-) –

+0

基数和选择性不是当量;一个位域具有大约50%的基数(null是可能性),但是在选择性上可能会有很大差异。有一些非常好的场景,在单比特字段上索引会证明是一个很大的好处。除了对位字段进行索引之外,SQL Server 2008+还可以对字段的一个值进行过滤,并创建一个只标识您感兴趣的记录的小索引。 – Suncat2000

1

虽然我认为尼尔芬威克的答案可能是正确的,但我认为真正的答案是尝试不同的选项,看看哪一个足够快。

选项1可能是最直接的解决方案,因此可能是最容易维护的 - 并且它可能足够快。

我将建立一个原型数据库,与“选项1”模式,并使用类似http://www.red-gate.com/products/sql-development/sql-data-generator/http://sourceforge.net/projects/dbmonster/创造两倍的数据,你预计需要,然后构建您预计需要查询。同意一个可接受的响应时间,并且如果超过了这些响应时间,则只考虑一个“更快”的模式(并且不能在硬件问题上抛出硬件)。

Neil的解决方案可能与“选项1”一样明显且易于维护 - 并且应该很容易进行索引。然而,我仍然通过创建一个原型架构并生成大量的测试数据来测试它...

+0

测试各种选项肯定会发生;我主要是保持这个问题的时间更长一些,以防有人可以提出另一个我们没有想到的替代方案。 –

+0

出于好奇,在几百万条记录进行了一些相对快速的测试之后,选项A和B似乎表现相对较好(都下降到一次索引搜索操作),而尼尔的一个速度慢了约40倍,并且超出范围对于用户可接受(20秒+)。该选项的执行计划要复杂得多,包括散列连接等。很多数据也被物理复制(通过设计),导致表和索引比选项A和B大很多倍。看到任何方式以更有效的方式索引它。 –