MySQL需要6秒才能计算100k记录中的条件

问题描述 投票:0回答:2
SELECT
  `id`, `code`, `description`, `minamt`
FROM `coupons`
WHERE
     `starts`<=DATE_FORMAT(NOW(),"%Y-%m-%d")
   AND
     `ends`>=DATE_FORMAT(NOW(),"%Y-%m-%d")
   and
      active=1
   and
      is_public=1

这个mysql需要6到7秒才能执行,因为优惠券表中有100k条记录

表结构

CREATE TABLE IF NOT EXISTS `coupons` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `bulk_coupon` int(11) DEFAULT '0',
  `ctype` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Type',
  `code` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT 'n/a' COMMENT 'Code',
  `discount` float(10,2) NOT NULL DEFAULT '0.00' COMMENT 'Discount',
  `description` text COLLATE utf8_bin,
  `minamt` float(10,2) NOT NULL DEFAULT '0.00' COMMENT 'Min. amount',
  `custlogin` tinyint(1) NOT NULL DEFAULT '2' COMMENT 'Requires customer login',
  `freeshipping` tinyint(1) NOT NULL DEFAULT '2' COMMENT 'Free shipping',
  `customer` text COLLATE utf8_bin,
  `products` text COLLATE utf8_bin COMMENT 'Specific products',
  `categories` text COLLATE utf8_bin COMMENT 'Spedific categories',
  `aod` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Apply on discounted products',
  `starts` date NOT NULL COMMENT 'Start on',
  `ends` date NOT NULL COMMENT 'Ends on',
  `is_public` tinyint(1) DEFAULT '0',
  `active` tinyint(1) DEFAULT '2' COMMENT 'Active',
  `usage_type` tinyint(1) DEFAULT '0',
  `is_used` tinyint(1) DEFAULT '0',
  `cod_applicable` tinyint(1) DEFAULT '0',
  `return_policy` tinyint(1) DEFAULT '1',
  `added` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `startEndDate` (`starts`,`ends`,`is_public`,`active`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1201682 ;
mysql query-optimization
2个回答
1
投票

简化:

+------------+-------------------------------+
| CURDATE()  | DATE_FORMAT(NOW(),"%Y-%m-%d") |
+------------+-------------------------------+
| 2019-02-19 | 2019-02-19                    |
+------------+-------------------------------+

需要的索引(优化程序将选择一个或另一个):

INDEX(active, is_public, start)
INDEX(active, is_public, end)

不要使用FLOATDOUBLE作为货币。使用DECIMAL


0
投票

查询重写可能是。

询问

SELECT 
    coupons.id 
  , coupons.code
  , coupons.description
  , coupons.minamt

FROM (

  SELECT
    coupons.id
  FROM coupons
  WHERE
       coupons.starts <= DATE_FORMAT(NOW(),"%Y-%m-%d")
     and
        coupons.active=1
     and
       coupons.is_public=1  
) AS coupons_start
INNER JOIN 
 coupons 
ON
  coupons_start.id = coupons.id
AND
     coupons.starts <= DATE_FORMAT(NOW(),"%Y-%m-%d")
   AND
     coupons.ends >= DATE_FORMAT(NOW(),"%Y-%m-%d")

这个似乎有一个“更好”的执行计划,您的查询。 请记住,空桌上的执行计划并不真实。 所以你需要在你自己的MySQL上做EXPLAINS来验证

EXPLAIN 

SELECT
  `id`, `code`, `description`, `minamt`
FROM `coupons`
WHERE
     `starts`<=DATE_FORMAT(NOW(),"%Y-%m-%d")
   AND
     `ends`>=DATE_FORMAT(NOW(),"%Y-%m-%d")
   and
      active=1
   and
      is_public=1

;

| id  | select_type | table   | type  | possible_keys | key          | key_len | ref | rows | Extra       |
| --- | ----------- | ------- | ----- | ------------- | ------------ | ------- | --- | ---- | ----------- |
| 1   | SIMPLE      | coupons | range | startEndDate  | startEndDate | 3       |     | 1    | Using where |

注意key_len只有3,这意味着查询只能使用startEndDate密钥的一小部分

EXPLAIN 
    SELECT 
        coupons.id 
      , coupons.code
      , coupons.description
      , coupons.minamt

    FROM (

      SELECT
        coupons.id
      FROM coupons
      WHERE
           coupons.starts <= DATE_FORMAT(NOW(),"%Y-%m-%d") 
    ) AS coupons_start
    INNER JOIN 
     coupons 
    ON
      coupons_start.id = coupons.id
    AND
         coupons.starts <= DATE_FORMAT(NOW(),"%Y-%m-%d")
       AND
         coupons.ends >= DATE_FORMAT(NOW(),"%Y-%m-%d")
       AND
         coupons.active = 1
       AND
         coupons.is_public = 1          

| id  | select_type | table   | type  | possible_keys | key          | key_len | ref | rows | Extra                                               |
| --- | ----------- | ------- | ----- | ------------- | ------------ | ------- | --- | ---- | --------------------------------------------------- |
| 1   | PRIMARY     |         |       |               |              |         |     |      | Impossible WHERE noticed after reading const tables |
| 2   | DERIVED     | coupons | index | startEndDate  | startEndDate | 10      |     | 1    | Using where; Using index                            |

就像我真的说过,在空桌上得到解释并不是很合理。 注意优化器在这里知道Impossible WHERE noticed after reading const tables表是空的。但是还要注意key_len是10并且具有索引类型和使用索引,这意味着内部查询可以从索引文件中单独获取id的连接所需的信息。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.