MySQL查询优化:如何加快查询速度?

时间:2014-05-27 18:13:47

标签: mysql sql query-optimization

这是我的表:

  CREATE TABLE `tab_adasf` (
  `adasf_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `adasf_shopId` int(10) unsigned NOT NULL,
  `adasf_localId` bigint(20) unsigned NOT NULL,
  `adasf_shopState` varchar(255) DEFAULT NULL,
  `adasf_shopCity` varchar(255) DEFAULT NULL,
  `adasf_shopName` varchar(255) DEFAULT NULL,
  `adasf_shopDoor` varchar(255) DEFAULT NULL,
  `adasf_computerName` varchar(255) DEFAULT NULL,
  `adasf_channel` bigint(20) NOT NULL,
  `adasf_totalInside` bigint(20) NOT NULL,
  `adasf_totalOutside` bigint(20) NOT NULL,
  `adasf_createdAt` datetime NOT NULL,
  PRIMARY KEY (`adasf_id`),
  KEY `adasf_shopId` (`adasf_shopId`),
  KEY `adasf_localId` (`adasf_localId`),
  KEY `adasf_shopState` (`adasf_shopState`,`adasf_shopCity`,`adasf_shopName`,`adasf_shopDoor`),
  KEY `adasf_computerName` (`adasf_computerName`,`adasf_channel`,`adasf_createdAt`),
  CONSTRAINT `tab_adasf_ibfk_1` FOREIGN KEY (`adasf_shopId`) REFERENCES `tab_shop` (`shop_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1453500 DEFAULT CHARSET=utf8

正如AUTO_INCREMENT的值所示:它有1453500行。

为了生成XML文件,我需要结果集如下:

SELECT
    UPPER(adasf_shopState) AS adasf_shopState,
    UPPER(adasf_shopCity) AS adasf_shopCity,
    UPPER(adasf_shopName) AS adasf_shopName,
    UPPER(adasf_shopDoor) AS adasf_shopDoor,
    adasf_computerName,
    adasf_channel,
    SUM(adasf_totalInside) AS adasf_totalInside,
    SUM(adasf_totalOutside) AS adasf_totalOutside,
    YEAR(adasf_createdAt) AS year,
    MONTH(adasf_createdAt) AS month,
    DAY(adasf_createdAt) AS day,
    HOUR(adasf_createdAt) AS hour
    FROM tab_adasf 
WHERE 1=1 AND adasf_shopId = '1' AND HOUR(adasf_createdAt) BETWEEN '10:00' AND '21:00' 
GROUP BY
    UPPER(adasf_shopState),
    UPPER(adasf_shopCity),
    UPPER(adasf_shopName),
    UPPER(adasf_shopDoor),
    adasf_computerName,
    adasf_channel,
    YEAR(adasf_createdAt),
    MONTH(adasf_createdAt),
    DAY(adasf_createdAt),
    HOUR(adasf_createdAt)
ORDER BY
    UPPER(adasf_shopState),
    UPPER(adasf_shopCity),
    UPPER(adasf_shopName),
    UPPER(adasf_shopDoor),
    UPPER(adasf_computerName),
    adasf_channel,
    adasf_createdAt

运行和获取需要3分钟。

我的问题是:我做错了什么?如何加快查询或表格的速度?

提前致谢!

3 个答案:

答案 0 :(得分:2)

要加快查询速度,您可以在tab_adasf(adasf_shopId)上创建索引。如果你有很多商店,这应该有助于提高性能。

如果您需要执行此类型的大量查询,请考虑将adasf_createdAt列拆分为日期组件和时间组件。然后,您可以在tab_adasf(adasf_shopId, adasf_createdAt_time)上创建索引,进一步帮助查询。

除非您有充分的理由,否则建议不要将时间与日期时间分开。提高此类查询的性能是一个“好理由”。

答案 1 :(得分:1)

正如其他人所说的那样,报告整个表的这样的查询本来就非常耗时。话虽如此,这里有一两个建议。

首先,消除UPPER()子句中的GROUP BY函数调用。无论如何,MySQL的排序规则都不区分大小写。

其次,而不是GROUP BY年,月,日,小时,尝试在GROUP BY中使用此表达式。

 DATE_FORMAT(adasf_createdAt, '%Y-%m-%d %H:00:00')

这基本上将您的createdAt值四舍五入到前一个小时。

第三,让我们重构一下WHERE项目

 HOUR(adasf_createdAt) BETWEEN '10:00' AND '21:00'

应该说

 HOUR(adasf_createdAt) BETWEEN 10 AND 21

如果您从主查询中删除它,它将加快速度。然后,您可以将查询包装在另一个查询中,如下所示:

SELECT *
  FROM ( /*your whole query without the WHERE HOUR() BETWEEN clause */
       ) AS q
  WHERE q.hour BETWEEN 10 AND 21

最后,尝试在

上创建覆盖索引的化合物
 adasf_shopId, adasf_shopState, adasf_shopCity, adasf_shopName, adasf_shopDoor,
 adasf_computerName, adasf_channel,  adasf_CreatedAt, 
 adasf_totalInside, adasf_totalOutside

此索引包含按顺序排列的满足查询所需的所有信息。这可能会加快您的查询速度。

因此,您的最终查询如下所示:

SELECT *
  FROM (
SELECT
       UPPER(adasf_shopState) AS adasf_shopState,
       UPPER(adasf_shopCity) AS adasf_shopCity,
       UPPER(adasf_shopName) AS adasf_shopName,
       UPPER(adasf_shopDoor) AS adasf_shopDoor,
       adasf_computerName,
       adasf_channel,
       SUM(adasf_totalInside) AS adasf_totalInside,
       SUM(adasf_totalOutside) AS adasf_totalOutside,
       YEAR(adasf_createdAt) AS year,
       MONTH(adasf_createdAt) AS month,
       DAY(adasf_createdAt) AS day,
       HOUR(adasf_createdAt) AS hour
 FROM tab_adasf 
WHERE 1=1 
  AND adasf_shopId = '1'  
GROUP BY
      adasf_shopState,
      adasf_shopCity,
      adasf_shopName,
      adasf_shopDoor,
      adasf_computerName,
      adasf_channel, 
      DATE_FORMAT(adasf_createdAt, '%Y-%m-%d %H:00:00')
ORDER BY
      adasf_shopState,
      adasf_shopCity,
      adasf_shopName,
      adasf_shopDoor,
      adasf_computerName,
      adasf_channel, 
      DATE_FORMAT(adasf_createdAt, '%Y-%m-%d %H:00:00')
     ) AS q
  WHERE q.hour BETWEEN 10 AND 21

您的查询简化以及覆盖索引可以使查询更快。

请注意,我还没有调试此查询,也没有测试数据。

答案 2 :(得分:-1)

编辑:这个答案在MySQL中不起作用。

您必须使用全表扫描检查每一行,看它是否与HOUR(adasf_createdAt) BETWEEN '10:00' AND '21:00'匹配。

HOUR(adasf_createdAt)上创建功能索引。

另外,请参阅http://use-the-index-luke.com以获取有关如何有效使用索引的优秀教程。