MySQL花费6秒计算100k条记录中的条件

时间:2019-02-19 10:21:16

标签: mysql query-optimization

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秒,因为优惠券表中有10万条记录

表结构

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 ;

2 个答案:

答案 0 :(得分:1)

简化:

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

需要索引(Optimizer将选择其中一个):

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

请勿使用FLOATDOUBLE作为货币。使用DECIMAL

答案 1 :(得分: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上进行解释

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所要加入的信息。