我有一个查询在WHERE
子句中添加了几个列值。我无法在单个列中预先计算此添加项,因为要使用的列组合因查询而异。我的问题是我的桌子非常大(几亿行)并且表现非常糟糕。
示例表:
+---------+------------+--------+--------+--------+--------+
| tableId | categoryId | value1 | value2 | value3 | value4 |
+---------+------------+--------+--------+--------+--------+
| 1 | 1 | 1 | 0 | 5 | 7 |
| 2 | 1 | 8 | 1 | 7 | 0 |
| 3 | 1 | 10 | 5 | 0 | 20 |
| 4 | 2 | 0 | 15 | 0 | 22 |
| 5 | 2 | 20 | 0 | 11 | 0 |
+---------+------------+--------+--------+--------+--------+
示例查询:
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value2 + value3 + value4) > 9;
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value3 + value4) > 5;
提高此类查询效果的最佳策略是什么? (编辑:我已经在categoryId
上有一个索引,这没有帮助)
使用索引帮助进行此类查询吗?那么我是否必须为所有可能的列组合创建所有可能的索引?结果索引不会非常大吗?
ALTER TABLE myTable
ADD INDEX(categoryId, value1),
ADD INDEX(categoryId, value2),
ADD INDEX(categoryId, value3),
ADD INDEX(categoryId, value4),
ADD INDEX(categoryId, value1, value2),
ADD INDEX(categoryId, value1, value3),
ADD INDEX(categoryId, value1, value4),
etc
或者也许创建一个链接表,使用布尔值字段指定使用哪些列?但这会产生一个有几十亿行的表,不确定这是否更好......
+---------+-----------+-----------+-----------+-----------+----------+
| tableId | useValue1 | useValue2 | useValue3 | useValue4 | valueSum |
+---------+-----------+-----------+-----------+-----------+----------+
| 1 | 1 | 1 | 1 | 1 | 13 |
| 1 | 1 | 1 | 1 | 0 | 6 |
| 1 | 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 0 | 1 | 8 |
| 1 | 1 | 0 | 1 | 1 | 13 |
| 1 | 1 | 0 | 1 | 0 | 6 |
| 1 | 1 | 0 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 | 8 |
| 1 | 0 | 1 | 1 | 1 | 12 |
| 1 | 0 | 1 | 1 | 0 | 5 |
| 1 | 0 | 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 0 | 1 | 7 |
| 1 | 0 | 0 | 1 | 1 | 12 |
| 1 | 0 | 0 | 1 | 0 | 5 |
| 1 | 0 | 0 | 0 | 1 | 7 |
+---------+-----------+-----------+-----------+-----------+----------+
使用索引:
ALTER TABLE linkTable INDEX(tableId, useValue1, useValue2, useValue3, useValue4, valueSum);
还有其他想法吗?
答案 0 :(得分:0)
@ e4c5是正确的,没有任何索引可以帮助当前查询。您可以首先添加以下索引并使用其他条件更改查询,以便使用索引:
ALTER TABLE myTable
ADD INDEX(categoryId, value1),
ADD INDEX(categoryId, value2),
ADD INDEX(categoryId, value3),
ADD INDEX(categoryId, value4);
并像这样更新查询:
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 9) AND (value2 <= 9) AND (value3 <= 9) AND (value4 <= 9) AND (value1 + value2 + value3 + value4) > 9;
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 5) AND (value3 <= 5) AND (value4 <= 5) AND (value1 + value3 + value4) > 5;
附加条件有助于缩小要处理的行数。在更多列上添加索引可以进一步提高速度,但我建议先尝试这一点。
答案 1 :(得分:0)
我必须做出一些猜测,直到我看到SHOW CREATE TABLE
......
如果你有这个:
tableId INT UNSIGNED AUTO_INCREMENT NOT NULL,
categoryId INT UNSIGNED NOT NULL,
...
PRIMARY KEY(tableId),
然后改为
tableId INT UNSIGNED AUTO_INCREMENT NOT NULL, -- same
categoryId INT UNSIGNED NOT NULL, -- same
...
PRIMARY KEY(categoryId, tableId), -- different, see Note 1
INDEX(tableId) -- different, see Note 2
注意1.以categoryId
开头的索引(PK)将有助于您提出的查询。此外,通过处于PK的开头,它将会#&#34; cluster&#34;一个SELECT
的所有必要行,从而最大限度地减少了巨大的表中的I / O.
注意2.是的,INDEX(...)
只能AUTO_INCREMENT
。
另一个提示......因为BIGINT
总是8个字节而INT
是4个字节;你真的需要那么大的专栏吗?缩小列大小将有助于减少I / O,这将显着加快查询速度。 MEDIUMINT UNSIGNED
只有3个字节,范围为0..16M;等
答案 2 :(得分:0)
根据my follow-up question about the overall database design中的答案,结论如下:
答案 3 :(得分:-1)
您可以将查询分类。对于每个类别,您可以保留预先计算的列。您可以根据所需的计算组合从表中选择相关字段。当然,您可以对查询进行分类。