【问题标题】:mysql select query optimization [closed]mysql选择查询优化[关闭]
【发布时间】:2012-11-10 21:41:52
【问题描述】:

我有两个表 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

【问题讨论】:

  • 从 cmets 到其他答案,如果您可以显示一些您尝试获取的示例数据(不暴露实际隐私类型信息),将会更有帮助。另外,澄清“A”与“B”表的预期内容。如果我们知道这个抽象的上下文,它也可能有助于替代解决方案。
  • testa 和 testb 表分别有多少行?
  • testa - 7600 & testb - 11000
  • 还有一个问题:如果您仅使用 3 个字段中的一个字段(如aid1)加入,它是否运行速度快并使用索引?换句话说,EXPLAIN ANALYZE SELECT a.id, a.name, b.name, b.id FROM testb b INNER JOIN testa a ON b.aid1 = a.id 是否显示使用索引?
  • 是的,它使用索引 idx_aid1。但是当我将所有三列与 OR 一起使用时,它会花费太多时间。

标签: mysql performance select query-optimization


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 我已经更新了我的表并将 0 设置为三列的默认值并更新了当前数据,但查询仍然需要花费太多时间来执行。
  • 你能用aid[123]把它分成3个独立的查询然后UNION ALL全部3个吗?
  • 正如我已经在问题中指定的那样,我曾在查询中尝试使用 UNION,但如果我使用 UNION,我的查询也会使用包括 testa 和 testb 在内的 13 个表执行。所以我无法在查询中使用 UNION。
  • 等一下!家长查询?如果你希望得到任何答案,你应该解释你所拥有的一切。这就像去看医生而不告诉你正在服用其他药物。如果您想保护数据隐私 - 当然可以,但不要遗漏重要细节。另外,请注意我要求UNION ALL,它与UNION 不同。
  • 感谢您的帮助,但对不起我的朋友,我无法透露我的父母查询。我确信这部分查询花费了太多时间来执行。
【解决方案2】:

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

【讨论】:

  • 我已经分析了两个表格,所有统计数据都很完美。
【解决方案3】:

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

  `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);

【讨论】:

  • 我已经在该列上添加了索引,但找不到太大的差异
  • 你能在这个查询中发布结果吗EXPLAIN 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
【解决方案4】:

您是否尝试过加入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) ;

【讨论】:

  • 仍然查询花费了太多时间。
  • 但是有什么改善吗?
  • 没有。执行需要更多时间
  • 很高兴知道。这只是一个建议(未经测试)。
  • 感谢您的建议,如果您发现一些有趣的东西可以解决此问题,请告诉我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-22
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多