SQL Server中多个位字段的索引

时间:2011-08-19 08:05:12

标签: sql sql-server sql-server-2008 indexing

我们目前有一个场景,其中一个表实际上有几个(10到15)布尔标志(不是可空的bit字段)。不幸的是,在逻辑层面上实际上不太可能简化这一点,因为布尔值的任何组合都是允许的。

有问题的表是一个事务表,最终可能有数千万行,插入和选择性能都非常关键。虽然我们目前还不太确定数据的分布,但所有标志的组合应提供相对良好的基数,即使其成为SQL Server使用的“有价值”索引。

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

鉴于这种情况,有效索引这些字段的推荐方法是什么?该表是新的,因此目前还没有现成的数据需要担心,我们在实际执行表时有相当大的灵活性。

目前我们正在考虑两个主要选项:

  • 创建一个包含所有位字段的索引(这可能包括总是使用的1或2个其他int字段)。我担心的是,鉴于仅包含一些字段的典型用法,此方法将跳过索引并采用表扫描。让我们称之为选项A (看过一些回复后,似乎这种方法效果不好,因为索引中字段的顺序会有所不同,因此无法有效地索引所有领域)。
  • 有效地执行我认为SQL Server在内部执行的操作,并使用二元运算符将位字段编码为单个int字段(AND-ing和OR-ing一起使用:1,2,4,8等)。我在这里担心的是我们需要进行某种计算来查询这个编码字段,这会再次跳过索引。此解决方案的维护和复杂性也是一个问题。我们称之为选项B 其他信息 这种方法的参数是我们可以有一个相对简单和简短的索引,其中包括表和此字段中的一个或两个其他字段。其他字段将缩小需要评估的记录数量,并且由于编码字段将包含所有位字段,因此SQL Server将能够使用从索引中直接检索的数据执行计算(即索引扫描) )而不是表(即表扫描)。

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

非常感谢任何建议。

修改:拼写,清晰度,查询示例,选项B 的其他信息。

3 个答案:

答案 0 :(得分:6)

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

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

麻烦的是:位列的序列很重要。如果您的SQL语句至少使用最左侧BIT列的1-n,那么您的索引将

所以如果您的索引在

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

然后它可能用于使用

的查询
  • Col1
  • Col1Col2
  • Col1以及Col2Col3 ....

等等。但无法用于指定Col6,Col9Col14的查询。

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

这15个BIT列是您用于查询的唯一列吗?如果没有,我会尝试将您最常用的BIT列与其他列结合使用,例如:有NameCol7之类的索引(那么你的BIT列可以为另一个索引添加一些额外的选择性)

答案 1 :(得分:3)

虽然可能有办法解决现有表架构的索引问题,但我会将其减少为规范化问题:

例如,我强烈建议您创建一系列新表:

  1. 查找表,了解此位标志的名称。例如CREATE TABLE Flags (id int IDENTITY(1,1), Name varchar(256))(如果你想手动控制id,你不必将id作为身份种子列 - 例如2,4,8,16,32,64,128作为二进制标志。)
  2. 创建一个新的链接表,其中包含原始数据表的ID和新的链接表,例如CREATE TABLE DataFlags_Link (id int IDENTITY(1,1), MyFlagId int, DataId int)
  3. 然后,您可以在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维护所在。您需要在表上适当地设置INDEX填充因子和填充,并按计划运行常规索引碎片整理或重建索引。

    性能和维护与数据库齐头并进。你不能没有另一个。

答案 2 :(得分:1)

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

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

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

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