有没有比为每个排列创建索引更好的方法来索引多列?

时间:2009-07-07 03:11:37

标签: mysql database indexing

假设我有一个包含a,b和c列的数据库表。我计划对所有三列进行查询,但我不确定哪些列特别是我要查询。表中有足够的行,索引极大地加快了搜索速度,但是制作可能索引的所有排列(如下所示)感觉不对:

a
b
c
a, b
a, c
b, c
a, b, c

有没有更好的方法来处理这个问题? (我很可能只是单独索引a,b,c,因为这会快速减少行数,但我想知道是否有更好的方法。)

如果您需要更具体的示例,在现实生活中的数据中,列是城市,州和邮政编码。另外,我正在使用MySQL数据库。

5 个答案:

答案 0 :(得分:19)

在MS SQL中,索引“a,b,c”将涵盖场景“a”; “a,b”;和“a,b,c”。所以你只需要以下索引:

a, b, c
b, c
c

不确定MySQL是否以相同的方式工作,但我会这么认为。

答案 1 :(得分:4)

要在N列上使用所有可能的相等条件的索引,您需要C([N/2], N)个索引,即N! / ([N/2]! * (N - [N/2])!)

有关详细说明,请参阅我的博客中的这篇文章:

您还可以阅读俄语数学家Egor Timoshenko的严格数学proof更新:现在用英语)。

然而,使用以下技术可以用较少的索引获得不错的性能:

索引合并

如果列col1col2col3具有选择性,那么此查询

SELECT  *
FROM    mytable
WHERE   col1 = :value1
        AND col2 = :value2
        AND col3 = :value3

可以在col1col2col3上使用三个单独的索引,分别选择与每个条件匹配的ROWID,并找到它们的交集,如:

SELECT  *
FROM    (
        SELECT  rowid
        FROM    mytable
        WHERE   col1 = :value1
        INTERSECT
        SELECT  rowid
        FROM    mytable
        WHERE   col2 = :value2
        INTERSECT
        SELECT  rowid
        FROM    mytable
        WHERE   col3 = :value3
        ) mo
JOIN    mytable mi
ON      mi.rowid = mo.rowid

位图索引

PostgreSQL可以在查询期间在内存中构建临时位图索引。

位图索引是一个非常紧凑的连续位数组。

为数组设置的每个位都表示应该从表中选择相应的tid

对于128M行的表,这样的索引只能占用1G临时存储空间。

以下查询:

SELECT  *
FROM    mytable
WHERE   col1 = :value1
        AND col2 = :value2
        AND col3 = :value3

首先会分配一个足够大的零填充位图,以覆盖表格中所有可能的tid(足够大,可以将所有tid(0, 0)带到最后一次,不考虑遗失tid。)

然后它将寻找第一个索引,如果满足第一个条件,则将位设置为1

然后它将使用AND扫描第二个索引,1'满足第二个条件的位。这将使1仅用于满足两个条件的那些位。

第三个索引相同。

最后,它只会选择tid对应于设置位的行。

tid将按顺序提取,因此非常有效。

答案 2 :(得分:1)

您创建的索引越多,您在更新和删除操作期间的性能就越高。因为索引本身可能会更新。

是的,您可以使用多列索引。像

这样的东西
CREATE TABLE temp (
    id         INT NOT NULL,
    a          INT NULL,
    b          INT NULL,
    c          INT NULL,
    PRIMARY KEY (id),
    INDEX ind1 (a,b,c),
    INDEX ind2 (a,b)
);

这种类型的索引,即ind1肯定会帮助你进行像

这样的查询
SELECT * FROM temp WHERE a=2 AND b=3 AND c=4;

同样,ind2将帮助您进行查询,例如

SELECT * FROM temp WHERE a=2 AND b=3;

但是如果查询类似于

,则不会使用这些索引
SELECT * FROM temp WHERE a=2 OR b=3 OR c=4;

在这里,你需要在a,b和c上使用单独的索引。

因此,我不同意这么多索引,我同意John所说的,即在a,b,c上有索引,如果你觉得你的工作量包含更多的多列查询,那么你可以切换到多列索引。

欢呼声

答案 3 :(得分:1)

鉴于您的列实际上是城市,州和邮政编码,我建议只使用以下索引:

<强> INDEX(邮编)

如果我是正确的,那么邮政编码在美国并不重复,因此向索引添加城市或州信息毫无意义,因为它们对于所有邮政编码都是相同的值。例如,90210总是洛杉矶,加利福尼亚州。

INDEX(City(5)) INDEX(City(5)),State)

这只是城市名称前五个字母的索引。在许多情况下,这将足够具体,使State索引不会提供任何有用的过滤。例如,'Los A'几乎肯定会是来自加利福尼亚州洛杉矶的记录。也许在美国还有另一个以“洛杉矶A”开头的小镇,但是会有这么少的记录,不值得用状态数据来混淆索引。另一方面,一些城市名称出现在许多州(想到斯普林菲尔德),因此在这些情况下,最好还要将州编入索引。您需要自己弄清楚哪个索引最适合您的数据集。如果有疑问,我会选择第二个指数(城市和州)。

INDEX(州, sort_field

是一个非常广泛的索引(很可能只有NY和CA将拥有30%的记录)。如果您计划向用户显示此信息,例如,一次显示30条记录,那么您的查询将以

结尾
... WHERE STATE = "NY"
ORDER BY <sort_field>
LIMIT <number>, 30

要使 查询有效,您需要在State索引中包含排序列。因此,如果您显示按姓氏排序的页面(假设您有该列),那么您将使用 INDEX(State,LastName(3)),否则MySQL必须排序所有< / strong>之前的'NY'记录可以为您提供所需的30个。

答案 4 :(得分:1)

这取决于你的sql-query。

指数(a,b,c)指数(b,c,a)指数(a,c,b)不同