Sql 为大型数据库中的简单查询编制索引

Sql 为大型数据库中的简单查询编制索引,sql,sql-server,database-design,data-structures,indexing,Sql,Sql Server,Database Design,Data Structures,Indexing,我得到了一个包含近85000000行的表 该表包含以下字段: [ID] [bigint] IDENTITY(1,1) NOT NULL, [D1] [int] NOT NULL, [D2] [int] NOT NULL, [D3] [int] NOT NULL, [D4] [int] NOT NULL, [D5] [int] NOT NULL, [D6] [int] NOT NULL, [D7] [int] NOT NULL, [D8] [int] NOT NULL, [D9] [int] NO

我得到了一个包含近85000000行的表

该表包含以下字段:

[ID] [bigint] IDENTITY(1,1) NOT NULL,
[D1] [int] NOT NULL,
[D2] [int] NOT NULL,
[D3] [int] NOT NULL,
[D4] [int] NOT NULL,
[D5] [int] NOT NULL,
[D6] [int] NOT NULL,
[D7] [int] NOT NULL,
[D8] [int] NOT NULL,
[D9] [int] NOT NULL,
[A] [int] NOT NULL,
[Hb] [bit] NOT NULL,
我对这个表的所有查询都是一样的-

选择[D1-D9],[A],其中[Hb]=0和[D1]x以及[D2]y和[D3]=z,

等等

每次查询将始终查询所有[D1-D9]字段,并始终要求[Hb]=0

查询示例:

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE [D1] <> 8 AND [D2] <> 2 AND [D3] <> 5 AND [D4] = 8 AND [D5] = 2 
   AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 AND [D9] = 4 AND [A] = 0 AND [Hb] = 0
选择[D1]、[D2]、[D3]、[D4]、[D5]、[D6]、[D7]、[D8]、[D9]、[A]
从[myTable]
其中[D1]8和[D2]2和[D3]5和[D4]=8和[D5]=2
[D6]=5和[D7]=5和[D8]=3和[D9]=4和[A]=0和[Hb]=0
我应该如何索引此表以获得最快的结果


非常感谢

如果您的算法是确定性的(即a=f(d1、d2、d3…d9)),那么您的D列与Hb组合构成一个键。尝试在所有D列和Hb上创建一个聚集复合索引,在Hb上进行分区以稍微提高速度。您也可以考虑删除ID字段。

编辑:
刚意识到我错过了比赛条件。正如其他人提到的,这让事情变得更加困难。这里您真正想要使用的是位图索引,但AFAIK SQL Server没有。您可能需要依靠单独的列索引配合使用。

首先,具有“where X 8”等条件的查询可能会使任何索引无效(这可能取决于实际的数据库引擎)

更安全的做法是从

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE [D1] <> 8 AND [D2] <> 2 AND [D3] <> 5 AND [D4] = 8 AND [D5] = 2 
   AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 AND [D9] = 4 AND [A] = 0 AND [Hb] = 0
选择[D1]、[D2]、[D3]、[D4]、[D5]、[D6]、[D7]、[D8]、[D9]、[A]
从[myTable]
其中[D1]8和[D2]2和[D3]5和[D4]=8和[D5]=2
[D6]=5和[D7]=5和[D8]=3和[D9]=4和[A]=0和[Hb]=0
更像这样:

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE ([D1] < 8 or [D1] > 8) 
       AND ([D2] < 2 or [D2] > 2) 
       AND ([D3] < 5 or [D3] > 5) 
       AND [D4] = 8 AND [D5] = 2  AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 
       AND [D9] = 4 AND [A] = 0 AND [Hb] = 0
Column  Selectivity  Cumulative selectivity
D4      0.96         0.96
D8      0.87         0.84
D9      0.85         0.70
D7      0.72         0.51
D6      0.65         0.33 -- here
D5      0.20         0.07
A       0.02         0.00
Hb      0.01         0.00
选择[D1]、[D2]、[D3]、[D4]、[D5]、[D6]、[D7]、[D8]、[D9]、[A]
从[myTable]
式中([D1]<8或[D1]>8)
和([D2]<2或[D2]>2)
和([D3]<5或[D3]>5)
[D4]=8和[D5]=2和[D6]=5和[D7]=5和[D8]=3
[D9]=4和[A]=0和[Hb]=0

最好先让索引执行相等检查,然后再执行剩余的不相等查找。也就是说,在
之前执行
=

重新排列WHERE条款:

WHERE
--Equality
D4 = 8 AND D5 = 2 AND D6 = 5 AND D7 = 5 AND D8 = 3 AND D9 = 4 AND A = 0 
--in the middle    
AND Hb = 0
--Non-Equality
D1 <> 8 AND D2 <> 2 AND D3 <> 5
D4
D9
的顺序应基于选择性。首先是最高数字。
Hb
在相等列中应始终排在最后,因为它是位

SELECT
   COUNT(DISTINCT D4) AS D4COunt,
   COUNT(DISTINCT D5) AS D5COunt,
   COUNT(DISTINCT D6) AS D6COunt,
   COUNT(DISTINCT D7) AS D7COunt,
   COUNT(DISTINCT D8) AS D8COunt,
   COUNT(DISTINCT D9) AS D9COunt,
   COUNT(DISTINCT A) AS ACOunt
FROM
    Mytable

最后,它可以是聚集的或非聚集的。如果没有其他索引或者没有FKS,那么我会考虑把它变成群集PK。否则,只需创建一个集群代理键,使这个索引非聚集

编辑:

这篇文章(希望)解释了为什么列顺序对多个列索引很重要:。还有他的

编辑2:

我问
之前的
=
是否在同一列上:它显示为“是”。 OP对这个答案的评论是“不”,所以我在这里所说的一切都是毫无意义的


Damien_的答案是不信者建议的索引交叉点,试图绕过相等/非相等的混合。

您可能会发现(如果每个查询中的十列的单个相等/不相等测试不同)最好的办法是在每一列上分别建立一个狭窄的索引,然后希望优化器能够应用,在有意义的情况下,它将在每一列上使用索引。

基本上,您应该创建以相等检查列开始的复合索引。因此,在您的情况下,使用[Hb]是很自然的作为第一个组成部分,因为您声明[Hb]将被相等地检查。索引的下一个元素是[D*],后面是[A]

create index IXC_MyTable1 on Mytable(Hb, D1, D2, D3, D4, D5, D6, D7, D8, D9, A)
再想一想,您可以使用部分索引,让db对表进行快速索引扫描(CMIIW)以检查其他值。在这种情况下,您应该将Id作为索引的最后一项。例如:

create index IXC_MyTable__D123 on Mytable(Hb, D1, D2, D3, Id)
create index IXC_MyTable__D456 on Mytable(Hb, D4, D5, D6, Id)
create index IXC_MyTable__D789 on Mytable(Hb, D7, D8, D9, Id)

当查询在Hb、D1、D2和D3等上使用相等检查时,将使用IXC_MyTable_uuD123索引。

扩展@gbn的答案

对于这种大小的表,您肯定需要一个涵盖所有选定列的索引

但是,对于每一列,您应该决定是希望它成为索引中的键列还是包含列

CREATE INDEX ix_mytable_filter ON (Hb, A, D5) INLCUDE (D1, D2, D3, D4, D6, D7, D8, D9)
要执行此操作,请运行以下查询:

SELECT  SUM(CASE D1 WHEN 8 THEN 0 ELSE 1 END) / COUNT(*) AS D1Card,
        SUM(CASE D2 WHEN 2 THEN 0 ELSE 1 END) / COUNT(*) / COUNT(DISTINCT D2) AS D2Card,
        SUM(CASE D3 WHEN 5 THEN 0 ELSE 1 END) / COUNT(*) / COUNT(DISTINCT D3) AS D3Card,
        SUM(CASE d4 WHEN 8 THEN 1 ELSE 0 END) / COUNT(DISTINCT D4) AS D4Card,
        SUM(CASE d5 WHEN 2 THEN 1 ELSE 0 END) / COUNT(DISTINCT D5) AS D5Card,
        SUM(CASE d6 WHEN 5 THEN 1 ELSE 0 END) / COUNT(DISTINCT D6) AS D6Card,
        SUM(CASE d7 WHEN 5 THEN 1 ELSE 0 END) / COUNT(DISTINCT D7) AS D7Card,
        SUM(CASE d8 WHEN 3 THEN 1 ELSE 0 END) / COUNT(DISTINCT D8) AS D8Card,
        SUM(CASE d9 WHEN 4 THEN 1 ELSE 0 END) / COUNT(DISTINCT D9) AS D9Card,
        SUM(CASE a WHEN 0 THEN 1 ELSE 0 END) / COUNT(DISTINCT A) AS ACard,
        SUM(CASE Hb WHEN 0 THEN 1 ELSE 0 END) / COUNT(DISTINCT Hb) AS HbCard
FROM    Mytable
您应该创建一个选择最少的列(那些具有最高值的
*Card
)的列表,这些列(加在一起)占您记录的25%以上

例如,列上的选择性图表如下所示:

SELECT [D1], [D2], [D3], [D4], [D5], [D6],[D7], [D8],[D9], [A] 
  from [myTable] 
 WHERE ([D1] < 8 or [D1] > 8) 
       AND ([D2] < 2 or [D2] > 2) 
       AND ([D3] < 5 or [D3] > 5) 
       AND [D4] = 8 AND [D5] = 2  AND [D6] = 5 AND [D7] = 5 AND [D8] = 3 
       AND [D9] = 4 AND [A] = 0 AND [Hb] = 0
Column  Selectivity  Cumulative selectivity
D4      0.96         0.96
D8      0.87         0.84
D9      0.85         0.70
D7      0.72         0.51
D6      0.65         0.33 -- here
D5      0.20         0.07
A       0.02         0.00
Hb      0.01         0.00
这意味着列
d4、d8、d9、d7、d6
上的条件一起匹配记录的
33%

在这种情况下,不需要将它们用作键列。您应该在其他选择性列上创建索引,并将非选择性列包括在索引中

CREATE INDEX ix_mytable_filter ON (Hb, A, D5) INLCUDE (D1, D2, D3, D4, D6, D7, D8, D9)
带有非相等筛选器的列始终转到
包含
部分

请注意,它只会使用给定的筛选器值改进当前查询。如果筛选器是任意的,则需要使用所有相等筛选列作为索引的键

CREATE INDEX ix_mytable_filter ON (Hb, A, D5) INLCUDE (D1, D2, D3, D4, D6, D7, D8, D9)
类似于
[D1]8
的条件也可能涉及幻数,并且该条件适用的记录很少

在这种情况下,可以将计算列添加到表的定义中:

ALTER TABLE mytable ADD d1_ne_8 AS CASE D1 WHEN 8 THEN 0 ELSE 1 END
并将此表达式添加到索引中(关于上述规则)


如果您这样做,您将不得不使用
d1_ne_8=1
而不是
d1 8

有多少行的hb=0和Dx等于每个数字?列的值分布如何?值是否经常更新?是否有任何列(除ID列外)具有递增值(从而成为聚集索引的候选)?您查询数据的方式是否有任何模式?如果您能给我们一些