3

我有两个表 testa 和 testb。

CREATE TABLE `testa` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `testb` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) DEFAULT NULL,
  `aid1` INT(10) DEFAULT NULL,
  `aid2` INT(10) DEFAULT NULL,
  `aid3` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

目前,我正在运行以下查询,以检索 testa 表中的 id 与 tableb 中的aid1、aid2、aid3 的任何列匹配的所有行。该查询正在检索准确的结果,但执行至少需要 30 秒,这太多了。我也尝试使用 UNION 优化我的查询,但没有这样做。

SELECT a.id, a.name, b.name, b.id 
FROM testb b 
INNER JOIN testa a ON b.aid1 = a.id OR b.aid2 = a.id OR b.aid3 = a.id ;

如何优化我的查询,使其总执行时间在 2-3 秒内?

提前致谢...

解释的结果:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  b   ALL idx_aid1,idx_aid2,idx_aid3  (NULL)  (NULL)  (NULL)  10940   
1   SIMPLE  a   ALL PRIMARY (NULL)  (NULL)  (NULL)  7512    Using where; Using join buffer
4

4 回答 4

4

因为您允许aid1、aid2、aid3 为NULL(显然,根据您的解释,它们大多为NULL),您的连接条件实际上是不可索引的。

为什么?如果aid1、aid2 或aid3 中的任何一个为NULL, SQL 表达式b.aid1 = a.id OR b.aid2 = a.id OR b.aid3 = a.id 的计算结果为NULL,这就是MySQL 规划器不显示使用索引的原因。

解决方案:不要对aid1、aid2、aid3 使用NULL。相反,发明特殊的 id(比如 0),它保证在 testa 中不存在。然后,确保 testb.aid[123] 不为 NULL(并将其分配给 0,之前为 NULL)。

编辑:为这个问题添加替代方法。

如果您有能力通过添加一个表来更改架构,您也可以解决此问题。这个新表将包含您当前存储在表 testb 中的援助列表,而 testb 将只包含一个链接到新表的 id。这应该类似于此答案中解释的内容。这样做的另一个好处是您可以允许任意数量的援助(而不是现在只有 3 个)。

于 2012-11-23T08:05:37.760 回答
1

除了其他人建议的索引之外,请确保您ANALYZE的表,以便表上的统计信息是最新的。如果统计信息与表中的实际数据大相径庭,那么查询计划器将做出错误的选择。

于 2012-11-23T06:09:23.917 回答
0

您是否尝试过加入IN而不是OR

SELECT a.id, a.name, b.name, b.id FROM testb b INNER JOIN testa a ON a.id IN (b.aid1, b.aid2, b.aid3) ;

于 2012-11-23T08:12:30.417 回答
0

您应该索引以下列以避免全表扫描

  `aid1` INT(10) DEFAULT NULL,
  `aid2` INT(10) DEFAULT NULL,
  `aid3` INT(10) DEFAULT NULL,

如果你想改变表格

ALTER TABLE testb ADD INDEX (aid1);
ALTER TABLE testb ADD INDEX (aid2);
ALTER TABLE testb ADD INDEX (aid3);
于 2012-11-22T13:23:15.760 回答