我有一张包含近850,000,000行的表。
该表格包含以下字段:
[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,
我对此表的所有查询都完全相同 -
Select [D1-D9], [A] Where [Hb] = 0 AND [D1] <> x AND [D2] <> y AND [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
我应该如何索引此表以获得最快的结果?
非常感谢
答案 0 :(得分:4)
您可以做的最好的事情是让您的索引先进行相等性检查,然后再进行残差非等式查找。也就是=
之前的<>
。
重新排列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
所以,初稿是这样的:
CREATE .. INDEX ... ON (D4, D5, D6, D7, D8, D9, A, Hb, D1, D2, D3)
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
最后,这可以是群集的或非群集的。如果您没有其他索引或没有FK,那么我会考虑将其作为群集PK。否则,只需创建一个聚集的代理键并使该索引为NONCLUSTERED
编辑:
一篇文章(希望)解释了为什么列顺序对多个列索引很重要:Craig Freedman's Seek Predicates。而他的Scans and Seeks也是
EDIT2:
我问=
之前的<>
是否在同一列上:它显示为“是”。
OP对这个答案的评论说“不”,所以我在这里说的一切都是毫无意义的
Damien_The_Unbeliever建议的指数交叉点的答案,试图解决这个平等/不平等的混合。
答案 1 :(得分:2)
您可能会发现(如果每个查询中的十个列的各个相等/不等式测试不同),您可以做的最好是单独在每个列上构建一个窄索引,然后希望优化器将应用{{ 3}},它将使用每列有意义的索引。
答案 2 :(得分:2)
扩展 @gbn 的答案。
对于此大小的表,您肯定需要一个涵盖所有选定列的索引。
但是,对于每一列,您应该决定是将它作为索引中的键列还是包含列。
要执行此操作,请运行以下查询:
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%
。
比如说,列上的选择性图表如下所示:
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)
具有不相等过滤器的列始终转到INCLUDE
部分。
请注意,它只会使用过滤器的给定值来改进当前查询。如果过滤器是任意的,则需要使用所有相等过滤列作为索引的键。
也可能是像[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
。
答案 3 :(得分:0)
如果你的算法是确定性的(即A = f(d1,d2,d3 ...... d9))那么你的D列与Hb结合就构成了一个关键。尝试在所有D列和Hb上创建聚簇复合索引,在Hb上进行分区以获得一点速度提升。您也可以考虑删除ID字段。
编辑: 刚刚意识到我错过了&lt;&gt;条件。正如其他人提到的那样,事情变得更加艰难。你真正想要在这里使用的是位图索引,但AFAIK SQL Server没有它们。您可能需要依赖单个列索引才能很好地协同工作。
答案 4 :(得分:0)
首先查询条件如“其中X&lt;&gt; 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
更像这样的事情:
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
答案 5 :(得分:0)
基本上,您应该创建由具有相等性检查的列启动的复合索引。因此,在您的情况下,自然地使用[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__D123索引;等等。