Bug #62738 Index is not used if I use IS TRUE instead of = TRUE when filtering
Submitted: 14 Oct 2011 14:32 Modified: 16 Oct 2012 5:15
Reporter: Cyril SCETBON Email Updates:
Status: Won't fix Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S4 (Feature request)
Version:5.1.60, 5.5.16 OS:Any
Assigned to: CPU Architecture:Any
Tags: boolean, explain, INDEX

[14 Oct 2011 14:32] Cyril SCETBON
Description:
If I search for rows WHERE myboolean IS TRUE, MySQL does not use the index, while it uses it If I use WHERE myboolean = TRUE

The optimizer should rewrite "WHERE myboolean IS TRUE" as "WHERE myboolean=1" as it does when I use "WHERE myboolean = TRUE"

So, I got a difference when I search in a table with 75 millions of rows where only one row as the boolean set !

mysql> explain SELECT * FROM LgstApplication WHERE EEDNeedsRecalculation = TRUE\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: LgstApplication
         type: ref
possible_keys: eedRecalculationIndex
          key: eedRecalculationIndex
      key_len: 1
          ref: const
         rows: 1
        Extra: 
1 row in set (0.00 sec)

mysql> explain SELECT * FROM LgstApplication WHERE EEDNeedsRecalculation IS TRUE\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: LgstApplication
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 73946933
        Extra: Using where
1 row in set (0.00 sec)

How to repeat:
should be easy no ????

mysql> create table t(id int, b boolean default FALSE);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t(id) select 1;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 32 rows affected (0.00 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 64 rows affected (0.00 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union select id from t union select id from t;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union all select id from t union all select id from t;
Query OK, 387 rows affected (0.00 sec)
Records: 387  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union all select id from t union all select id from t;
Query OK, 1548 rows affected (0.02 sec)
Records: 1548  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union all select id from t union all select id from t;
Query OK, 6192 rows affected (0.03 sec)
Records: 6192  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union all select id from t union all select id from t;
Query OK, 24768 rows affected (0.18 sec)
Records: 24768  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t union all select id from t union all select id from t;
Query OK, 99072 rows affected (0.43 sec)
Records: 99072  Duplicates: 0  Warnings: 0

mysql> alter table t add index(b);
Query OK, 0 rows affected (0.22 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> insert into t values(10,TRUE);
Query OK, 1 row affected (0.00 sec)

mysql> explain select count(*) from t where b is true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: index
possible_keys: NULL
          key: b
      key_len: 2
          ref: NULL
         rows: 131783
        Extra: Using where; Using index
1 row in set (0.00 sec)

mysql> explain select count(*) from t where b = true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: ref
possible_keys: b
          key: b
      key_len: 2
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

Suggested fix:
the fastest patch would be to rewrite "IS TRUE" as "= 1" like it is with "= TRUE"

mysql> explain extended select count(*) from t where b is true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: index
possible_keys: NULL
          key: b
      key_len: 2
          ref: NULL
         rows: 131783
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql> show warnings \G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select count(0) AS `count(*)` from `jrep_data_qalperf`.`t` where (`jrep_data_qalperf`.`t`.`b` is true)
1 row in set (0.00 sec)

mysql> explain extended select count(*) from t where b = true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: ref
possible_keys: b
          key: b
      key_len: 2
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql> show warnings \G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select count(0) AS `count(*)` from `jrep_data_qalperf`.`t` where (`jrep_data_qalperf`.`t`.`b` = 1)
1 row in set (0.00 sec)
[14 Oct 2011 14:46] Valeriy Kravchuk
Thank you for the bug report. Verified just as described:

macbook-pro:5.1 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 4
Server version: 5.1.60 Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

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

mysql> create table t(id int, b boolean default FALSE);
Query OK, 0 rows affected (0.13 sec)

mysql> insert into t(id) select 1;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into t(id) select id from t;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql>  alter table t add index(b);
Query OK, 32 rows affected (0.18 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> insert into t values(10,TRUE);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t;
+------+------+
| id   | b    |
+------+------+
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|    1 |    0 |
|   10 |    1 |
+------+------+
33 rows in set (0.00 sec)

mysql> explain select count(*) from t where b is true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: index
possible_keys: NULL
          key: b
      key_len: 2
          ref: NULL
         rows: 33
        Extra: Using where; Using index
1 row in set (0.03 sec)

mysql> explain select count(*) from t where b = true\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
         type: ref
possible_keys: b
          key: b
      key_len: 2
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

Index is used in both cases, but it is "ref" access for one row vs "index" (entire index scan).
[16 Oct 2012 5:15] Erlend Dahl
Analysis from dev team: This would have been a valid and useful feature request if MySQL supported the data type boolean.

However, when creating a table and specifying a boolean data type, the server
interprets this as synonym for tinyint(1). According to the manual, "Nonzero
values are considered true" for such data types, so it will not be possible
for us to transform the expression "b IS TRUE" into "b = 1", because it will
miss all values that are not zero and not 1.