Bug #18165 | SQL optimizer ineficiencies with "BETWEEN" statements | ||
---|---|---|---|
Submitted: | 12 Mar 2006 12:12 | Modified: | 10 Oct 2006 18:51 |
Reporter: | Martin Sperl | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
Version: | 5.0.21-BK, 4.1.14, 5.0.18 | OS: | Linux (Linux) |
Assigned to: | Igor Babaev | CPU Architecture: | Any |
[12 Mar 2006 12:12]
Martin Sperl
[22 Apr 2006 15:59]
Valeriy Kravchuk
Verified with 5.0.21-BK on Linux: mysql> CREATE TABLE `EventTest` ( -> `EventID` bigint(20) NOT NULL default '0', -> `BBServerID` bigint(20) NOT NULL default '0', -> `ServerID` bigint(20) NOT NULL default '0', -> `ServiceID` bigint(20) NOT NULL default '0', -> `IPID` bigint(20) NOT NULL default '0', -> `StartTime` datetime NOT NULL default '0000-00-00 00:00:00', -> `EndTime` datetime NOT NULL default '0000-00-00 00:00:00', -> `Position` enum('current','historic') NOT NULL default 'historic', -> `StateID` bigint(20) NOT NULL default '0', -> `BBAgentID` bigint(20) NOT NULL default '0', -> `QServerInfoID` bigint(20) NOT NULL default '0', -> KEY `ServiceIndex` (`Position`,`ServerID`,`ServiceID`,`BBServerID`), -> KEY `BBServerID` (`BBServerID`,`ServerID`,`ServiceID`,`StartTime`), -> KEY `Position` (`Position`,`StateID`), -> KEY `StartTime` (`StartTime`,`EndTime`), -> KEY `EndTime` (`EndTime`) -> ) ENGINE=MyISAM DEFAULT CHARSET=latin1; Query OK, 0 rows affected (0.01 sec) mysql> insert into `EventTest` (`EventID`) values (1), (2), (3), (4), (5), (6); Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0 mysql> insert into `EventTest` (`StartTime`, `EndTime`) values('2006-03-11 11:00:00', '2006-03-11 11:00:02'); Query OK, 1 row affected (0.00 sec) mysql> explain extended select * FROM EventTest WHERE "2006-03-11 11:00:00" BETWEEN StartTime and EndTime; +----+-------------+-----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | EventTest | ALL | NULL | NULL | NULL | NULL | 7 | Using where | +----+-------------+-----------+------+---------------+------+---------+------+------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings\G *************************** 1. row *************************** Level: Note Code: 1003 Message: select `test`.`EventTest`.`EventID` AS `EventID`,`test`.`EventTest`.`BBServerID` AS `BBServerID`,`test`.`EventTest`.`ServerID` AS `ServerID`,`test`.`EventTest`.`ServiceID` AS `ServiceID`,`test`.`EventTest`.`IPID` AS `IPID`,`test`.` EventTest`.`StartTime` AS `StartTime`,`test`.`EventTest`.`EndTime` AS `EndTime`, `test`.`EventTest`.`Position` AS `Position`,`test`.`EventTest`.`StateID` AS `StateID`,`test`.`EventTest`.`BBAgentID` AS `BBAgentID`,`test`.`EventTest`.`QServerInfoID` AS `QServerInfoID` from `test`.`EventTest` where (_latin1'2006-03-11 11:00:00' between `test`.`EventTest`.`StartTime` and `test`.`EventTest`.`EndTime`) 1 row in set (0.01 sec) mysql> explain extended select * FROM EventTest WHERE "2006-03-11 11:00:00" >= StartTime AND "2006-03-11 11:00:00" <= EndTime; +----+-------------+-----------+-------+-------------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+-------+-------------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | EventTest | range | StartTime,EndTime | EndTime | 8 | NULL | 2 | Using where | +----+-------------+-----------+-------+-------------------+---------+---------+------+------+-------------+ 1 row in set, 1 warning (0.01 sec) mysql> show warnings\G *************************** 1. row *************************** Level: Note Code: 1003 Message: select `test`.`EventTest`.`EventID` AS `EventID`,`test`.`EventTest`.`BBServerID` AS `BBServerID`,`test`.`EventTest`.`ServerID` AS `ServerID`,`test`.`EventTest`.`ServiceID` AS `ServiceID`,`test`.`EventTest`.`IPID` AS `IPID`,`test`.` EventTest`.`StartTime` AS `StartTime`,`test`.`EventTest`.`EndTime` AS `EndTime`, `test`.`EventTest`.`Position` AS `Position`,`test`.`EventTest`.`StateID` AS `StateID`,`test`.`EventTest`.`BBAgentID` AS `BBAgentID`,`test`.`EventTest`.`QServerInfoID` AS `QServerInfoID` from `test`.`EventTest` where ((20060311110000 >= `test`.`EventTest`.`StartTime`) and (20060311110000 <= `test`.`EventTest`.`EndTime`) ) 1 row in set (0.00 sec) mysql> select version(); +-----------+ | version() | +-----------+ | 5.0.21 | +-----------+ 1 row in set (0.00 sec) I hope, the problem and possible solution is obvious from the above. Test case: CREATE TABLE `EventTest` ( `EventID` bigint(20) NOT NULL default '0', `BBServerID` bigint(20) NOT NULL default '0', `ServerID` bigint(20) NOT NULL default '0', `ServiceID` bigint(20) NOT NULL default '0', `IPID` bigint(20) NOT NULL default '0', `StartTime` datetime NOT NULL default '0000-00-00 00:00:00', `EndTime` datetime NOT NULL default '0000-00-00 00:00:00', `Position` enum('current','historic') NOT NULL default 'historic', `StateID` bigint(20) NOT NULL default '0', `BBAgentID` bigint(20) NOT NULL default '0', `QServerInfoID` bigint(20) NOT NULL default '0', KEY `ServiceIndex` (`Position`,`ServerID`,`ServiceID`,`BBServerID`), KEY `BBServerID` (`BBServerID`,`ServerID`,`ServiceID`,`StartTime`), KEY `Position` (`Position`,`StateID`), KEY `StartTime` (`StartTime`,`EndTime`), KEY `EndTime` (`EndTime`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; insert into `EventTest` (`EventID`) values (1), (2), (3), (4), (5), (6); insert into `EventTest` (`StartTime`, `EndTime`) values('2006-03-11 11:00:00', '2006-03-11 11:00:02'); EXPLAIN select * FROM EventTest WHERE "2006-03-11 11:00:00" BETWEEN StartTime AND EndTime; EXPLAIN select * FROM EventTest WHERE "2006-03-11 11:00:00" >= StartTime AND "2006-03-11 11:00:00" <= EndTime; Two statements above should give the same results according to the definition of BETWEEN.
[28 Jul 2006 2:37]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/9692
[16 Aug 2006 16:37]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/10556 ChangeSet@1.2259, 2006-08-16 09:37:19-07:00, igor@rurik.mysql.com +4 -0 Fixed bug #18165. Made [NOT]BETWEEN predicates SARGable in respect to the second and the third arguments.
[29 Aug 2006 13:23]
Evgeny Potemkin
Fixed in 5.0.25
[4 Sep 2006 11:38]
Evgeny Potemkin
Fixed in 5.1.12
[16 Sep 2006 19:07]
Sergey Petrunya
Paul, we use "Sargable" as defined here: http://en.wikipedia.org/wiki/Sargable "a condition (or predicate) in a query is said to be sargable if the DBMS engine can take advantage of an index to speed up the execution of the query (using index seeks, not covering indexes)". Before this fix BETWEEN was Sargable only w.r.t 1st argument, i.e. the only sargable form for t.keyX was: t.keyX BETWEEN a AND b now the optimizer can also use those forms : a BETWEEN t.keyX AND b (e.g. from here it infers t.keyX <=a) a BETWEEN b AND t.keyX .
[10 Oct 2006 18:51]
Paul DuBois
Noted in 5.0.25, 5.1.12 changelogs. The optimizer did not take advantage of indexes on columns used for the second or third arguments of BETWEEN.