| Bug #34310 | Optimizer is inefficient by using ORDER BY function() and LIMIT | ||
|---|---|---|---|
| Submitted: | 5 Feb 2008 14:51 | Modified: | 18 Dec 2013 14:03 | 
| Reporter: | Richard Teubel | Email Updates: | |
| Status: | Verified | Impact on me: | |
| Category: | MySQL Server: Optimizer | Severity: | S5 (Performance) | 
| Version: | 5.0.51a-log, 5.0.54, 5.6.14 | OS: | Linux | 
| Assigned to: | CPU Architecture: | Any | |
| Tags: | FUNCTION, limit, order by, query | ||
   [5 Feb 2008 18:24]
   Valeriy Kravchuk        
  Thank you for a problem report. Verified just as described also with 5.0.54:
C:\Program Files\MySQL\MySQL Server 5.0\bin>mysql -uroot -proot -P3308 test
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 5.0.54-enterprise-gpl-nt-log MySQL Enterprise Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> CREATE TABLE IF NOT EXISTS `A` (
    ->   `ID` int(11) unsigned NOT NULL auto_increment,
    ->   `Value` varchar(20) NOT NULL,
    ->   PRIMARY KEY  (`ID`)
    -> ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
Query OK, 0 rows affected (0.14 sec)
mysql>
mysql> INSERT INTO `A` (`ID`, `Value`) VALUES
    -> (1, 'Q;ls(#/''VE:KgC#^S'),
    -> (2, ' Dqs#._'),
    -> (3, 'G!c'),
    -> (4, 'W"(l?P'),
    -> (5, '24KrL5PL<7JVJ`.n'),
    -> (6, 'F<FgmKWY'),
    -> (7, ':u]qu4=re2{b'),
    -> (8, 'iJvIq7Va'),
    -> (9, ')B7:'),
    -> (10, 'faC.]+Yw>VEc$F');
Query OK, 10 rows affected (0.01 sec)
Records: 10  Duplicates: 0  Warnings: 0
mysql> delimiter //
mysql> CREATE DEFINER=`root`@`%` FUNCTION `func_A`(param1 varCHAR(20)) RETURNS v
archar(20)
    -> CHARSET latin1
    ->     DETERMINISTIC
    -> BEGIN
    ->   RETURN param1;
    -> END //
Query OK, 0 rows affected (0.09 sec)
mysql> CREATE DEFINER=`root`@`%` FUNCTION `func_B`(param1 varCHAR(20)) RETURNS varchar(20)
    -> CHARSET latin1
    ->     DETERMINISTIC
    -> BEGIN
    ->   SELECT SLEEP(0.1) INTO @temp;
    ->
    ->   SET @testcount = @testcount + 1;
    ->
    ->   RETURN param1;
    -> END //
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> SET @testcount = 0;
Query OK, 0 rows affected (0.03 sec)
mysql> SELECT A.Value AS Order1,
    ->        func_A(A.Value) AS Order2,
    ->        func_B(A.Value) AS Order3
    -> FROM A
    -> ORDER BY Order1
    -> LIMIT 1,2;
+------------------+------------------+------------------+
| Order1           | Order2           | Order3           |
+------------------+------------------+------------------+
| )B7:             | )B7:             | )B7:             |
| 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n |
+------------------+------------------+------------------+
2 rows in set (0.25 sec)
mysql> SELECT @testcount;
+------------+
| @testcount |
+------------+
| 2          |
+------------+
1 row in set (0.00 sec)
mysql> explain SELECT A.Value AS Order1,
    ->        func_A(A.Value) AS Order2,
    ->        func_B(A.Value) AS Order3
    -> FROM A
    -> ORDER BY Order1
    -> LIMIT 1,2;
+----+-------------+-------+------+---------------+------+---------+------+-----
-+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows
 | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+----------------+
|  1 | SIMPLE      | A     | ALL  | NULL          | NULL | NULL    | NULL |   10
 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+----------------+
1 row in set (0.00 sec)
mysql> SET @testcount = 0;
Query OK, 0 rows affected (0.02 sec)
mysql>
mysql> SELECT A.Value AS Order1,
    ->        func_A(A.Value) AS Order2,
    ->        func_B(A.Value) AS Order3
    -> FROM A
    -> ORDER BY Order2
    -> LIMIT 1,2;
+------------------+------------------+------------------+
| Order1           | Order2           | Order3           |
+------------------+------------------+------------------+
| )B7:             | )B7:             | )B7:             |
| 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n |
+------------------+------------------+------------------+
2 rows in set (1.11 sec)
mysql> SELECT @testcount;
+------------+
| @testcount |
+------------+
| 10         |
+------------+
1 row in set (0.00 sec)
mysql> explain SELECT A.Value AS Order1,
    ->        func_A(A.Value) AS Order2,
    ->        func_B(A.Value) AS Order3
    -> FROM A
    -> ORDER BY Order2
    -> LIMIT 1,2;
+----+-------------+-------+------+---------------+------+---------+------+-----
-+---------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows
 | Extra                           |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+---------------------------------+
|  1 | SIMPLE      | A     | ALL  | NULL          | NULL | NULL    | NULL |   10
 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+---------------------------------+
1 row in set (0.00 sec)
I think EXPLAIN shows the difference (Using temporary in second case), but I still do not understand why for DETERMINISTIC functions there is any difference...
 
   [18 Dec 2013 14:03]
   Richard Teubel        
  This problem exists allready in version 5.6.14.


Description: Hello, i think the optimizer is inefficient by using ORDER BY function() and LIMIT. In my Table are 10 rows. func_A receive only the parameter. func_B receive it, too. But it's a slow function which sleep 100 ms and increment @testcount. If I use ORDER BY func_A and say LIMIT 1,2 for 2 rows then func_B has 10 passes with 1000 ms and not 2 passes with 200ms. First I show you the Query with Order By a TableColumn and the func_B has only 2 passes. SET @testcount = 0; SELECT A.Value AS Order1, func_A(A.Value) AS Order2, func_B(A.Value) AS Order3 FROM A ORDER BY Order1 LIMIT 1,2; +------------------+------------------+------------------+ | Order1 | Order2 | Order3 | +------------------+------------------+------------------+ | )B7: | )B7: | )B7: | | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | +------------------+------------------+------------------+ 2 rows in set (0.22 sec) <<<<===== 2rows, 200 ms it's Ok SELECT @testcount; +------------+ | @testcount | +------------+ | 2 | +------------+ 1 row in set (0.00 sec) Now, the same Query with ORDER BY func_A(A.Value) and the func_B has 10 passes. Very bad, it take 1000 ms. SET @testcount = 0; SELECT A.Value AS Order1, func_A(A.Value) AS Order2, func_B(A.Value) AS Order3 FROM A ORDER BY Order2 LIMIT 1,2; +------------------+------------------+------------------+ | Order1 | Order2 | Order3 | +------------------+------------------+------------------+ | )B7: | )B7: | )B7: | | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | 24KrL5PL<7JVJ`.n | +------------------+------------------+------------------+ 2 rows in set (1.04 sec) <<<<===== 2 rows, 1000 ms :( SELECT @testcount; +------------+ | @testcount | +------------+ | 10 | <<=== 8 passes too much. +------------+ 1 row in set (0.00 sec) How to repeat: CREATE TABLE IF NOT EXISTS `A` ( `ID` int(11) unsigned NOT NULL auto_increment, `Value` varchar(20) NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ; INSERT INTO `A` (`ID`, `Value`) VALUES (1, 'Q;ls(#/''VE:KgC#^S'), (2, ' Dqs#._'), (3, 'G!c'), (4, 'W"(l?P'), (5, '24KrL5PL<7JVJ`.n'), (6, 'F<FgmKWY'), (7, ':u]qu4=re2{b'), (8, 'iJvIq7Va'), (9, ')B7:'), (10, 'faC.]+Yw>VEc$F'); DELIMITER // CREATE DEFINER=`root`@`%` FUNCTION `func_A`(param1 varCHAR(20)) RETURNS varchar(20) CHARSET latin1 DETERMINISTIC BEGIN RETURN param1; END // CREATE DEFINER=`root`@`%` FUNCTION `func_B`(param1 varCHAR(20)) RETURNS varchar(20) CHARSET latin1 DETERMINISTIC BEGIN SELECT SLEEP(0.1) INTO @temp; SET @testcount = @testcount + 1; RETURN param1; END // DELIMITER ; -- -- SET @testcount = 0; SELECT A.Value AS Order1, func_A(A.Value) AS Order2, func_B(A.Value) AS Order3 FROM A ORDER BY Order1 LIMIT 1,2; SELECT @testcount; -- -- SET @testcount = 0; SELECT A.Value AS Order1, func_A(A.Value) AS Order2, func_B(A.Value) AS Order3 FROM A ORDER BY Order1 LIMIT 1,2; SELECT @testcount;