优化查询,去掉“Using where;Using temporary;Using filesort”

Posted

技术标签:

【中文标题】优化查询,去掉“Using where;Using temporary;Using filesort”【英文标题】:Optimise query to remove "Using where; Using temporary; Using filesort" 【发布时间】:2011-06-14 13:21:38 【问题描述】:

我有一个简单的 SQL 查询,我正在尝试优化它以删除“使用 where;使用临时;使用文件排序”。

这是桌子:

CREATE TABLE `special_offers` (
  `so_id` int(11) NOT NULL auto_increment,
  `so_lid` int(11) NOT NULL,
  `so_product_id` int(11) NOT NULL,
  `so_bonus_product` int(11) NOT NULL,
  `so_reverse_relate` tinyint(1) NOT NULL default '0',
  `so_discount_amount` varchar(6) NOT NULL,
  `so_start` date NOT NULL default '0000-00-00',
  `so_expiry` date NOT NULL default '0000-00-00',
  `so_active` tinyint(1) NOT NULL,
  `so_archived` tinyint(4) NOT NULL default '0',
  `so_added` datetime NOT NULL,
  PRIMARY KEY  (`so_id`),
  KEY `so_archived` (`so_archived`),
  KEY `so_active` (`so_active`),
  KEY `so_start` (`so_start`),
  KEY `so_expiry` (`so_expiry`),
  KEY `so_product_id` (`so_product_id`),
  KEY `so_bonus_product` (`so_bonus_product`),
  KEY `so_lid` (`so_lid`)
) ENGINE=MyISAM AUTO_INCREMENT=65610 DEFAULT CHARSET=latin1

这是查询:

SELECT `so_id` , `so_lid` , `so_bonus_product` , `so_product_id`
FROM `special_offers`
WHERE `so_archived` = '0'
AND `so_active` = '1'
AND (
`so_start` <= CURDATE( )
OR `so_start` = '0000-00-00'
)
AND (
`so_expiry` >= CURDATE( )
OR `so_expiry` = '0000-00-00'
)
GROUP BY `so_lid`

解释:

mysql> EXPLAIN SELECT `so_id` , `so_lid` , `so_bonus_product` , `so_product_id` FROM `special_offers` WHERE `so_archived` = '0' AND `so_active` = '1' AND ( `so_start` <= CURDATE( ) OR `so_start` = '0000-00-00' ) AND ( `so_expiry` >= CURDATE( ) OR `so_expiry` = '0000-00-00' ) GROUP BY `so_lid`;
+----+-------------+-------------------+------+------------------------------------------+-------------+---------+-------+------+----------------------------------------------+
| id | select_type | table             | type | possible_keys                            | key         | key_len | ref   | rows | Extra                                        |
+----+-------------+-------------------+------+------------------------------------------+-------------+---------+-------+------+----------------------------------------------+
|  1 | SIMPLE      | special_offers    | ref  | so_archived,so_active,so_start,so_expiry | so_archived | 1       | const | 7684 | Using where; Using temporary; Using filesort |
+----+-------------+-------------------+------+------------------------------------------+-------------+---------+-------+------+----------------------------------------------+

【问题讨论】:

如果每个so_lid 有多个记录,则此查询将返回每个so_lid 的任何(随机)记录(满足条件)。是你想要的吗? @Quassnoi - 你可能会在那里做些什么。查看围绕此的代码(我没有写),无论如何似乎都存在逻辑问题。 【参考方案1】:

(so_archived, so_active, so_lid, so_start, so_end)上创建复合索引

【讨论】:

我确实试过了,它没有改变任何东西,我需要删除现有的索引吗? @seengee:尝试使用FORCE INDEX,看看计划是否改变。还可以尝试收集统计信息 (ANALYZE TABLE special_offers)。一般来说,如果不访问实际数据,这些东西很难调试。 使用FORCE INDEX确实将查询切换到using where,但也将执行时间从0.0429秒推到0.1789秒。 ANALYZE TABLE special_offers 告诉我“表已经是最新的” @seengee:嗯,对不起,弄乱了索引顺序。请立即尝试。 是的,就是这样,查询执行相同,但现在“使用 where”。谢谢!无论如何都不相信代码中的实际逻辑是正确的,但这肯定回答了我的问题!【参考方案2】:

我有一句话,当使用日期函数时:

so_start` <= CURDATE( )

mysql不要在这个fieldso_start上使用索引,

尝试以服务器端语言输入真实日期

【讨论】:

CURDATE() 不是字段,而是函数。 MySQL 能够为此谓词使用索引。【参考方案3】:

我认为 group by 可能会给您带来问题。

SELECT  `so_id` , `so_lid` , `so_bonus_product` , `so_product_id` ... GROUP BY `so_lid`

当按so_lid 分组时,so_idso_bonus_productso_product_id 的值对于给定的so_lid 都相同。你可能会发现你得到了意想不到的结果。这也可能导致优化问题。

请参阅这篇文章。 http://dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html

【讨论】:

【参考方案4】:

我会在 (so_archived, so_active, so_lid) 上有一个复合索引并添加一个关键字...

SELECT STRAIGHT_JOIN ...其余查询

SELECT STRAIGHT_JOIN 
      `so_id` , `so_lid` , `so_bonus_product` , `so_product_id` 
  FROM 
      `special_offers` 
  WHERE 
          `so_archived` = '0' 
      AND `so_active` = '1' 
      AND ( `so_start` <= CURDATE( ) OR `so_start` = '0000-00-00' ) 
      AND ( `so_expiry` >= CURDATE( ) OR `so_expiry` = '0000-00-00' ) 
   GROUP BY 
      `so_lid` 

【讨论】:

@Quassnoi,“STRAIGHT_JOIN”是一个关键字,告诉查询优化器按照您声明的顺序执行查询。通过让 matchec 复合键上的“WHERE”条件更接近匹配,应该选择该索引。此外,它还非常适合与其他表进行连接,其中查询优化器可能会选择另一个表,因为它的行数较少并会降低性能。 ***.com/questions/313759/… @DRapp 仍然给我留下“在哪里使用;使用临时的;使用文件排序” @DRapp 根据链接到的其他问题,在这种情况下,直接连接似乎毫无意义 @DRapp: STRAIGHT_JOIN 仅强制在嵌套循环中读取表的顺序。由于查询中只有一张表,所以没有效果。 WHERE 子句中谓词的顺序不影响查询计划。

以上是关于优化查询,去掉“Using where;Using temporary;Using filesort”的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 执行计划中Extra(Using where,Using index,Using index condition,Using index,Using where)的浅析

EXPLAIN 中的“Using index”和“Using where; Using index”有啥区别

mysql 大数据量查询如何优化,没办法去掉<>和like

优化 Sql Query 去掉 SELECT 子句并使用 JOIN

一次移动优化之旅

oracle怎么优化