Bug #45969 the query otimizer choose the wrong index

Submitted: 6 Jul 2009 12:23 Modified: 8 Oct 2012 15:31
Reporter: Manfred Wiedemeier Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:5.1.35, 5.1.38-bzr OS:Windows (XP, Vista)
Assigned to: CPU Architecture:Any
Tags: regression

[6 Jul 2009 12:23] Manfred Wiedemeier
Description:
In version 5.0.77, the otimizer choose the index `recid_r02`, for the following select. In version 5.1.35 it's now `key6`.

If i change: ORDER BY r03.r_datum DESC
         to: ORDER BY r03.recid_r02, r03.r_datum DESC
the index is correct.

It seems that the optimizer did not mention that i did not use the field `r03.r_datum` in the where clause.

SELECT r02.*,
       (SELECT r03.r_datum 
          FROM r03 
          WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9))) 
          ORDER BY r03.r_datum DESC
          LIMIT 1
       ) AS last_order

  FROM r02
  WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)
  ORDER BY r02.auftrag DESC
  LIMIT 50

Tables:

CREATE TABLE `r02` (
  `recid`    int(11) NOT NULL DEFAULT '0',
  `deleted`  tinyint(1) NOT NULL DEFAULT '0',
  `auftrag`  int(11) NOT NULL DEFAULT '0',
  `nachtrag` int(11) NOT NULL DEFAULT '0',
  `kurzbez`  varchar(60) COLLATE latin1_general_ci NOT NULL,
  `flags`    bigint(20) NOT NULL DEFAULT '0',

  PRIMARY KEY (`recid`),
  UNIQUE  KEY `key` (`auftrag`,`nachtrag`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

CREATE TABLE `r03` (
  `recid`     int(11) NOT NULL DEFAULT '0',
  `recid_r02` int(11) NOT NULL DEFAULT '0',
  `deleted`   tinyint(1) NOT NULL DEFAULT '0',
  `auftrag`   int(11) NOT NULL DEFAULT '0',
  `nachtrag`  int(11) NOT NULL DEFAULT '0',
  `texttype`  int(11) NOT NULL DEFAULT '0',
  `nummer`    int(11) NOT NULL DEFAULT '0',
  `rechn_nr`  varchar(20) COLLATE latin1_general_ci DEFAULT NULL,
  `doku_nr`   varchar(20) COLLATE latin1_general_ci DEFAULT NULL,
  `betrag`    double NOT NULL DEFAULT '0',
  `r_datum`   date NOT NULL DEFAULT '0000-00-00',

  PRIMARY KEY (`recid`),
  UNIQUE KEY `key` (`auftrag`,`nachtrag`,`texttype`,`nummer`,`deleted`),
  UNIQUE KEY `key2` (`rechn_nr`),
  UNIQUE KEY `key3` (`doku_nr`),
  KEY `key6` (`r_datum`),
  KEY `recid_r02` (`recid_r02`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

How to repeat:
create the tables r02 and r03. 
I've got 30000 records in r02 and 90000 record in r03.
[6 Jul 2009 12:29] Valeriy Kravchuk
OK, different index is used (this is related to partial fix for bug #28404 we have in 5.1.x). But why do you think this is a bug? Do you have seriously slower execution?

Please, send also the results of:

SELECT recid_r02, count(*) FROM r03
GROUP BY recid_r02
ORDER BY 2 DESC LIMIT 10;

I want to know how many rows in the table may have particular recid_r02 value.
[20 Jul 2009 11:03] Manfred Wiedemeier
I've got 85.000 records in R02 and 80.000 in R03.
The execution time has changed from < 1 sec. to > 280 sec.

SELECT recid_r02, count(*) FROM r03
GROUP BY recid_r02
ORDER BY 2 DESC LIMIT 10;

recid_r02    count(*)
    27256        3361
    23569        3222
    19452        3155
    15370        3065
    12801        2407
    12796        1996
    12793        1859
        0         513
    12802         301
    29625         282
[22 Jul 2009 9:28] Sveta Smirnova
Thank you for the feedback.

I can not repeat described behavior: in case of MySQL-5.1 I get recid_r02  too. Please run OPTIMIZE TABLE on 5.1 server and inform us if it changed results.
[27 Jul 2009 8:36] Manfred Wiedemeier
I optimize the tables, same result. 
The optimizer choose index `key6` and not `recid_r02`.

If you like i can send you my database.
[27 Jul 2009 9:25] Sveta Smirnova
Thank you for the feedback.

> If you like i can send you my database.

Yes, this would be good.
[27 Jul 2009 11:26] Manfred Wiedemeier
I have uploaded "bug-data-45969.zip" to your ftp-server.
[30 Jul 2009 13:43] Valeriy Kravchuk
Verified just as described using your tables uploaded with latest 5.1.38-bzr on Linux:

openxs@suse:/home2/openxs/dbs/5.1> bin/mysql -uroot reg00003
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.1.38-debug Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+--------------------+
| Tables_in_reg00003 |
+--------------------+
| r02                |
| r03                |
+--------------------+
2 rows in set (0.01 sec)

mysql> explain SELECT r02.*,
    ->        (SELECT r03.r_datum
    ->           FROM r03
    ->           WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9)))
    ->           ORDER BY r03.r_datum DESC
    ->           LIMIT 1
    ->        ) AS last_order
    ->
    ->   FROM r02
    ->   WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)
    ->   ORDER BY r02.auftrag DESC
    ->   LIMIT 50
    ->
    -> ;
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
| id | select_type        | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
|  1 | PRIMARY            | r02   | range | key           | key  | 4       | NULL | 27056 | Using where |
|  2 | DEPENDENT SUBQUERY | r03   | index | recid_r02     | key6 | 3       | NULL |     1 | Using where |
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
2 rows in set (0.02 sec)

As you correctly noted index recid_r02 can be used:

openxs@suse:/home2/openxs/dbs/5.1> bin/mysql -uroot reg00003
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.1.38-debug Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+--------------------+
| Tables_in_reg00003 |
+--------------------+
| r02                |
| r03                |
+--------------------+
2 rows in set (0.01 sec)

mysql> explain SELECT r02.*,
    ->        (SELECT r03.r_datum
    ->           FROM r03
    ->           WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9)))
    ->           ORDER BY r03.r_datum DESC
    ->           LIMIT 1
    ->        ) AS last_order
    ->
    ->   FROM r02
    ->   WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)
    ->   ORDER BY r02.auftrag DESC
    ->   LIMIT 50
    ->
    -> ;
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
| id | select_type        | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
|  1 | PRIMARY            | r02   | range | key           | key  | 4       | NULL | 27056 | Using where |
|  2 | DEPENDENT SUBQUERY | r03   | index | recid_r02     | key6 | 3       | NULL |     1 | Using where |
+----+--------------------+-------+-------+---------------+------+---------+------+-------+-------------+
2 rows in set (0.02 sec)

mysql> explain SELECT r02.*,        (SELECT r03.r_datum            FROM r03            WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9)))            ORDER BY r03.recid_r02, r03.r_datum DESC           LIMIT 1        ) AS last_order    FROM r02   WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)   ORDER BY r02.auftrag DESC   LIMIT 50;
+----+--------------------+-------+-------+---------------+-----------+---------+--------------------+-------+-----------------------------+
| id | select_type        | table | type  | possible_keys | key       | key_len | ref                | rows  | Extra                       |
+----+--------------------+-------+-------+---------------+-----------+---------+--------------------+-------+-----------------------------+
|  1 | PRIMARY            | r02   | range | key           | key       | 4       | NULL               | 27056 | Using where                 |
|  2 | DEPENDENT SUBQUERY | r03   | ref   | recid_r02     | recid_r02 | 4       | reg00003.r02.RECID |     3 | Using where; Using filesort |
+----+--------------------+-------+-------+---------------+-----------+---------+--------------------+-------+-----------------------------+
2 rows in set (0.00 sec)

and, actually, query is much faster when it is used:

mysql> SELECT r02.*,        (SELECT r03.r_datum            FROM r03  FORCE INDEX(recid_r02)          WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9)))            ORDER BY r03.r_datum DESC           LIMIT 1        ) AS last_order    FROM r02   WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)   ORDER BY r02.auftrag DESC   LIMIT 50;
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
| RECID | DELETED | AUFTRAG | NACHTRAG | KURZBEZ                                          | FLAGS    | last_order |
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
| 12803 |       0 |   50001 |        0 | Mustervorlage fr Dokumentation                  |     4096 | NULL       |

...

| 30399 |       0 |   42209 |        0 | Studentenversion                                 |      768 | NULL       |
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
50 rows in set (0.48 sec)

mysql> SELECT r02.*,        (SELECT r03.r_datum            FROM r03            WHERE ((r03.recid_r02 = r02.recid) AND (r03.texttype IN (4,7,8,9)))            ORDER BY r03.r_datum DESC           LIMIT 1        ) AS last_order    FROM r02   WHERE (r02.auftrag > 0) AND (r02.deleted = FALSE)   ORDER BY r02.auftrag DESC   LIMIT 50;
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
| RECID | DELETED | AUFTRAG | NACHTRAG | KURZBEZ                                          | FLAGS    | last_order |
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
| 12803 |       0 |   50001 |        0 | Mustervorlage fr Dokumentation                  |     4096 | NULL       |

...

| 30399 |       0 |   42209 |        0 | Studentenversion                                 |      768 | NULL       |
+-------+---------+---------+----------+--------------------------------------------------+----------+------------+
50 rows in set (1 min 5.75 sec)

So, one minute instead of less than a second...
[21 Apr 2011 7:53] Daniƫl van Eeden
As noted in support request 3-3357778341:
- This also affects Linux x86-64, not only Microsoft Windows (XP,Vista)
- This also affects version 5.5.8 (Enterprise), not only 5.1.35, 5.1.38-bzr

Could this still be fixed in 5.5+ or should it target 5.6+?
[8 Oct 2012 15:31] Paul DuBois
Noted in 5.6.8, 5.7.0 changelogs.

For some queries involving ORDER BY, the optimizer chose the wrong
index for accessing the table.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.043 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.