Bug #9523 abuse of index in simple query

Submitted: 31 Mar 2005 13:15 Modified: 14 Jan 2010 16:40
Reporter: Vadim Tkachenko Email Updates:
Status: No Feedback Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S2 (Serious)
Version:5.0.9-beta, 4.1.10a OS:Windows (Windows XP)
Assigned to: CPU Architecture:Any

[31 Mar 2005 13:15] Vadim Tkachenko
Description:
I have two tables:

CREATE TABLE `t1` (
   `ID` int(11) default NULL,
   `VAL` varchar(5) default NULL,
   KEY `VAL` (`VAL`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
|   250001 |
+----------+

CREATE TABLE `t2` (
   `ID` int(11) NOT NULL auto_increment,
   `NAME` varchar(255) default NULL,
   PRIMARY KEY  (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

SELECT COUNT(*) FROM t2;
+----------+
| COUNT(*) |
+----------+
|   50001 |
+----------+

89.9% rows in t1 contain val='val1'

SELECT c2/c1
FROM (SELECT COUNT(*) c1 FROM t1) d1,
 (SELECT COUNT(*) c2 FROM t1 WHERE val='val1') d2;

0.89993

Query:
SELECT COUNT(DISTINCT t2.id)
FROM  t2 JOIN
      t1 USING(id)
WHERE t1.val='val1';
uses index VAL:

EXPLAIN SELECT COUNT(DISTINCT t2.id) FROM t2 JOIN t1 USING(id) WHERE 
t1.val='val1';

*************************** 1. row ***************************
            id: 1
   select_type: SIMPLE
         table: t1
          type: ref
possible_keys: VAL
           key: VAL
       key_len: 6
           ref: const
          rows: 107131
         Extra: Using where
*************************** 2. row ***************************
            id: 1
   select_type: SIMPLE
         table: t2
          type: eq_ref
possible_keys: PRIMARY
           key: PRIMARY
       key_len: 4
           ref: test.t1.ID
          rows: 1
         Extra: Using index
2 rows in set (0.00 sec)

Time of execution:
SELECT COUNT(DISTINCT t2.id) FROM t2 JOIN t1 USING(id) WHERE 
t1.val='val1';
(2453 ms taken)

Time of execution without index:
SELECT COUNT(DISTINCT t2.id) FROM t2 JOIN t1 IGNORE INDEX (VAL) USING(id) WHERE t1.val='val1';
(1250 ms taken)

How to repeat:
CREATE TABLE `t1` (
   `ID` int(11) default NULL,
   `VAL` varchar(5) default NULL,
   KEY `VAL` (`VAL`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `t2` (
   `ID` int(11) NOT NULL auto_increment,
   `NAME` varchar(255) default NULL,
   PRIMARY KEY  (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Scripts for t1 & t2 data:

<?

for ($i=0; $i <= 50000; $i++)
{
 echo "INSERT INTO t2 (name) VALUES ('test".md5(rand(1,5))."');\n";
}

?>

<?

echo "TRUNCATE TABLE t1;\n";

for ($i=0; $i <= 250000; $i++)
{
 echo "INSERT INTO t1 (id,val) VALUES (".rand(1,50000).",'".(rand(1,10)==10?'val2':'val1')."');\n";
}

echo "ANALYZE TABLE t1;\n";
echo "OPTIMIZE TABLE t1;\n";

?>

EXPLAIN SELECT COUNT(DISTINCT t2.id) FROM t2 JOIN t1 USING(id) WHERE 
t1.val='val1';
[11 May 2005 16:46] Igor Babaev
Employing a primitive cost model the optimizer sometimes selects not the fastest execution plan.
[19 Jul 2005 18:36] Vadim Tkachenko
I checked optimizer behavior with extremally simple test case:
CREATE TABLE `t2` (
`ID` int(11) default NULL,
`ID1` int(11) default NULL,
`SUBNAME` varchar(32) default NULL,
KEY `ID1` (`ID1`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

SELECT cnt2 / cnt1 FROM (SELECT count(*) cnt1 FROM t2) d1, (SELECT count(*) cnt2 FROM t2 WHERE ID1=1) d2;
0.9492 = 94.92%

mysql> EXPLAIN SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1;
+----+-------------+-------+------+---------------+------+---------+-------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+-------+-------------+
| 1 | SIMPLE | t2 | ref | ID1 | ID1 | 5 | const | 81371 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+-------+-------------+

i.e. optimizer wants to use index.

execution time:
SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1 - 1200 ms
SELECT COUNT(SUBNAME) FROM t2 IGNORE INDEX (ID1) WHERE ID1=1 - 260 ms

I think, whatever primitive optimizer we have, it should make right choice in this extremally simple case.
[2 Aug 2005 14:56] Vadim Tkachenko
In case with 99% (for table with 1 000 000 rows) selectivity
mysql> select c1/c2 from (select count(*) c1 from test.t2 where ID1=1) t1, (select count(*) c2 from test.t2) t2;
+--------+
| c1/c2  |
+--------+
| 0.9901 |
+--------+
still
mysql> explain select count(SUBNAME) from test.t2 where ID1=1\G                                                 
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
         type: ref
possible_keys: ID1
          key: ID1
      key_len: 5
          ref: const
         rows: 918909
        Extra: Using where

In case with 100% selectivity tablescan is used.
[14 Dec 2009 16:40] Valeriy Kravchuk
With recent mysql-6.0-codebase we have:

77-52-7-73:6.0-codebase openxs$ bin/mysql -uroot test
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 2
Server version: 6.0.14-alpha-debug Source distribution

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

mysql> CREATE TABLE `t2` (
    -> `ID` int(11) default NULL,
    -> `ID1` int(11) default NULL,
    -> `SUBNAME` varchar(32) default NULL,
    -> KEY `ID1` (`ID1`)
    -> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.07 sec)

mysql> insert into t2 values(rand()*10000, 1, '');
Query OK, 1 row affected (0.38 sec)

mysql> insert into t2 select rand()*10000, 1, '' from t2
    -> ;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, '' from t2;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, '' from t2;
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;
Query OK, 8 rows affected (0.02 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;
Query OK, 16 rows affected (0.01 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;
Query OK, 32 rows affected (0.00 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;
Query OK, 64 rows affected (0.01 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;
Query OK, 128 rows affected (0.01 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 2, 'a' from t2;
Query OK, 256 rows affected (0.01 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, 'a' from t2;
Query OK, 512 rows affected (0.02 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> SELECT cnt2 / cnt1 FROM (SELECT count(*) cnt1 FROM t2) d1, (SELECT count(*) cnt2 FROM t2
    -> WHERE ID1=1) d2;
+-------------+
| cnt2 / cnt1 |
+-------------+
|      0.7500 |
+-------------+
1 row in set (0.02 sec)

mysql> EXPLAIN SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1;
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
|  1 | SIMPLE      | t2    | ref  | ID1           | ID1  | 5       | const |  829 |       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
1 row in set (0.36 sec)

So, with 75% selectivity index is used. And maybe this is bad:

mysql> SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1;
+----------------+
| COUNT(SUBNAME) |
+----------------+
|            520 |
+----------------+
1 row in set (0.01 sec)

mysql> SELECT COUNT(SUBNAME) FROM t2 IGNORE INDEX (ID1) WHERE ID1=1;
+----------------+
| COUNT(SUBNAME) |
+----------------+
|            520 |
+----------------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1;
+----------------+
| COUNT(SUBNAME) |
+----------------+
|            520 |
+----------------+
1 row in set (0.01 sec)

mysql> insert into t2 select rand()*10000, 1, NULL from t2;Query OK, 1024 rows affected (0.05 sec)
Records: 1024  Duplicates: 0  Warnings: 0

mysql> insert into t2 select rand()*10000, 1, NULL from t2;Query OK, 2048 rows affected (0.11 sec)
Records: 2048  Duplicates: 0  Warnings: 0

mysql> SELECT cnt2 / cnt1 FROM (SELECT count(*) cnt1 FROM t2) d1, (SELECT count(*) cnt2 FROM t2 WHERE ID1=1) d2;
+-------------+
| cnt2 / cnt1 |
+-------------+
|      0.9375 |
+-------------+
1 row in set (0.01 sec)

mysql> insert into t2 select rand()*10000, 1, NULL from t2;Query OK, 4096 rows affected (0.24 sec)
Records: 4096  Duplicates: 0  Warnings: 0

mysql> SELECT cnt2 / cnt1 FROM (SELECT count(*) cnt1 FROM t2) d1, (SELECT count(*) cnt2 FROM t2 WHERE ID1=1) d2;
+-------------+
| cnt2 / cnt1 |
+-------------+
|      0.9688 |
+-------------+
1 row in set (0.03 sec)

mysql> EXPLAIN SELECT COUNT(SUBNAME) FROM t2 WHERE ID1=1;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t2    | ALL  | ID1           | NULL | NULL    | NULL | 8192 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

but when selectivity is 96.88% index is NOT used. Can we consider the problem solved?
[15 Jan 2010 0:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".

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

Page generated in 0.054 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.