优化ORDER BY查询

时间:2010-11-20 17:37:57

标签: mysql optimization query-optimization sql-order-by

我很茫然。我有一个大约100K行的表。查询此表时,结果通常很快,大约2ms左右。但每当我使用ORDER BY性能下降到大约120毫秒。我阅读了MySQL ORDER BY Optimization页面,但我不能说我理解了一切。特别是指数我不清楚。

最终我想运行以下查询:

SELECT *
  FROM `affiliate_new_contracts`
 WHERE  phone_brand IN ('Apple','Blackberry','HTC','LG','Motorola','Nokia',
                        'Samsung','Sony Ericsson')
   AND contract_length IN ('12','24')
   AND (addon IS NULL OR addon IN('Telfort Sms 300','Surf & Mail'))
   AND (plan_name = 'Telfort 100'
        AND 
        credible_shop = 1
       ) 
  ORDER BY average_price_per_month ASC, phone_price_guestimate DESC,
           contract_length ASC;

但如果我了解基本原则,我会很高兴 删除上一个查询中的ORDER BY子句使其在20ms内运行而不是120ms。我在average_price_per_month字段上有一个索引,但将ORDER BY子句简化为ORDER BY average_price_per_month没有产生性能提升。我不明白。我也对所谓的多列索引一无所知,它应该能够帮助我进行最终的查询。

任何帮助将不胜感激。我如何让这个坏男孩表演?或者是那个任务乌托邦?

CREATE TABLE语法如下:

$ show create table affiliate_new_contracts;
CREATE TABLE `affiliate_new_contracts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `contract_length` int(11) DEFAULT NULL,
  `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `price` float DEFAULT NULL,
  `average_price_per_month` float DEFAULT NULL,
  `phone_price_guestimate` float DEFAULT NULL,
  `credible_shop` tinyint(1) DEFAULT '0',
  `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `addon_price` float DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`),
  KEY `index_affiliate_new_contracts_on_average_price_per_month` (`average_price_per_month`),
  KEY `index_affiliate_new_contracts_on_price` (`price`)
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

BTW此表每周重新创建一次,同时不会更新。

2 个答案:

答案 0 :(得分:4)

您可以对ORDER BY子句进行多少优化是有限制的。有时帮助的主要方法是以正确的顺序在正确的列集上建立索引。因此,对于您的示例,(单个,复合)索引:

average_price_per_month ASC, phone_price_guestimate DESC, contract_length ASC

可能会有所帮助,但优化器可能仍然认为最好使用其他索引来处理查询中的过滤条件,然后它会对自己选择的数据进行排序。请注意,除非索引以完全正确的排序顺序提供数据,并且使用索引加速整个查询,否则优化程序将不会使用它。仅有一个要排序的列的索引对优化器有限,并且通常不会使用这样的索引。

需要考虑的一个问题是:

  • 没有ORDER BY子句,查询的执行速度有多快。

这使您可以直接测量分拣成本。你提到20毫秒没有订购和120毫秒订购,所以ORDER BY是适度昂贵的。接下来的问题可能是“你能在你的应用程序中胜过它的排序吗?”。您可以这样做,但DBMS中的排序包通常已经过相当好的优化,您可能需要努力工作才能击败它。

答案 1 :(得分:0)

我怀疑你的索引对你没有任何好处,因为它不是主键,你的查询选择逻辑(where子句)不使用它。因为您没有使用索引来选择哪些行,所以最终必须在选择后对结果进行排序。它不是您的主键这一事实意味着结果尚未按每月平均价格排序,这将减少或消除因为已经订购的排序时间。

一种解决方案是使用包含最具选择性的列(计划名称)和排序列(average_price_per_month)的复合索引。在选择之后仍然需要进行排序,但是主要排序列已经对结果进行了排序,减少了花费的时间。

CREATE TABLE `affiliate_new_contracts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `contract_length` int(11) DEFAULT NULL,
  `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `price` float DEFAULT NULL,
  `average_price_per_month` float DEFAULT NULL,
  `phone_price_guestimate` float DEFAULT NULL,
  `credible_shop` tinyint(1) DEFAULT '0',
  `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `addon_price` float DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`,`average_price_per_month`),
  KEY `index_affiliate_new_contracts_on_price` (`price`)
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

您可能还想使用EXPLAIN来了解查询的执行方式(如果我的直觉不正确)并相应地调整索引。