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 ;
简化:
+------------+-------------------------------+
| CURDATE() | DATE_FORMAT(NOW(),"%Y-%m-%d") |
+------------+-------------------------------+
| 2019-02-19 | 2019-02-19 |
+------------+-------------------------------+
需要的索引(优化程序将选择一个或另一个):
INDEX(active, is_public, start)
INDEX(active, is_public, end)
不要使用FLOAT
或DOUBLE
作为货币。使用DECIMAL
。
查询重写可能是。
询问
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的连接所需的信息。