索引列只要參與了計算, 查詢就會不走索引, 為什麼 MySQL 不對這種情況進行優化?

假設有一張名為 order 的表, 有 3 列, 其中 ctime 列有索引, 表結構如下:

查看執行計劃, 只有下面的 SQL (一) 會走索引, SQL (二) 會進行全表掃描.

SQL (一)

SELECT *
FROM `order`
WHERE ctime + INTERVAL 30 SECOND &< "2017-07-11 12:00:00" AND ctime + INTERVAL 300 SECOND &> "2017-07-11 12:00:00";

SQL (二)

SELECT *
FROM `order`
WHERE ctime &< "2017-07-11 12:00:00" - INTERVAL 30 SECOND AND ctime &> "2017-07-11 12:00:00" - INTERVAL 300 SECOND;

以上兩個 SQL 實際是等價的, 優化器只需要做一次簡單的變換就可以將 SQL (二) 優化為 SQL (一); 可實際上, MySQL 並不會做這個優化, 為什麼?

建表 SQL 如下:

## Create table
DROP TABLE IF EXISTS `order`;

CREATE TABLE `order` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(20) NOT NULL
COMMENT "用戶 id",
`ctime` DATETIME NOT NULL
COMMENT "創建時間",
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_ctime` (`ctime`)
)
ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4
COMMENT = "訂單";

## Create procedure
DROP PROCEDURE IF EXISTS batch_insert;

DELIMITER //

CREATE PROCEDURE batch_insert()
BEGIN
DECLARE v INT DEFAULT 1;
WHILE v &<= 1000 DO INSERT INTO `order` (user_id, ctime) VALUES (concat(v, "0000"), "2017-07-11 12:00:00" - INTERVAL (v * 60) SECOND); SET v = v + 1; END WHILE; END // CALL batch_insert() // DELIMITER ;


因為SQL沒實現的那麼聰明,僅此而已。今天你要我把a+b&

說來,我對題主的描述很感興趣,為啥不是圖二走索引?


題主的圖應該是放反了!

第一個是:全表掃描,實際是在MySQLServer中過濾,因為MySQL暫不支持函數索引;

第二個是:範圍掃描,同時使用了索引下推(ICP)。

版本: 5.7.18-log

PS:

吐槽下,題主的表名竟然是 `order` 而不是 order!


如果我是mysql的優化器,我就有句mmp要講


sql引擎可真心不是編譯器,能給你做的優化是有限度的。


LZ是不是寫反了。。。應該是第2個才走索引,第1個不走才對吧


為了給面試官提供面試題啊


你這貼反了,MySQL不支持左邊執行演算法操作走索引的


歪個樓

為啥不是查詢二走索引呢?

這個查詢就是range的形式啊。。


所以說寫一手高效的sql 也是需要功力的,知道清楚如何避免索引失效


推薦閱讀:

有什麼好的書籍了解sql執行細節?比如執行計劃之類的。
mysql表中查找和小於某個數的所有最前面的記錄?
以 MySQL 為例,如何進行 SQL 注入和防止被注入?
分享下你寫過的你覺得最厲害的sql語句?
請教一下大家,關於 MySQL 百萬數據量的 count(*) 查詢如何優化?

TAG:SQL | MySQL | SQL語句 | SQL優化 |