Bug #99775 Functional index and equality condition between two columns
Submitted: 4 Jun 2020 7:33 Modified: 4 Jun 2020 8:47
Reporter: Nikolai Ikhalainen Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.20 OS:Any
Assigned to: CPU Architecture:Any
Tags: functional key parts, generated columns

[4 Jun 2020 7:33] Nikolai Ikhalainen
Description:
If a table is created with:
create table t(a int, b int, KEY ((a=2*b)));

And filled only with few rows matching a=2*b,
The condition "where a=2*b" is not using functional index
The condition "(a=2*b)=1" uses the index.

How to repeat:
create table t(a int, b int, KEY ((a=2*b)));
INSERT INTO t (a,b) WITH RECURSIVE my_cte AS (   SELECT 1 AS n   UNION ALL   SELECT 1+n FROM my_cte WHERE n<900 ) SELECT n a, IF(RAND() < 0.01, n/2, n) b FROM my_cte;

select count(0) from t where a=2*b; -- not using index
select count(0) from t where (a=2*b)=1; -- index is used 

mysql> select count(0) from t where a=2*b;
+----------+
| count(0) |
+----------+
|        4 |
+----------+
1 row in set (0.00 sec)

mysql> explain select count(0) from t where a=2*b;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  900 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select count(0) from t where (a=2*b);
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  900 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select count(0) from t where (a=2*b)=1;
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t     | NULL       | ref  | functional_index | functional_index | 5       | const |    4 |   100.00 | NULL  |
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select count(0) from t where (a=2*b)=TRUE;
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t     | NULL       | ref  | functional_index | functional_index | 5       | const |    4 |   100.00 | NULL  |
+----+-------------+-------+------------+------+------------------+------------------+---------+-------+------+----------+-------+

Suggested fix:
Use functional index with boolean expressions.
Unfortunately there is no BOOLEAN type in MySQL and (a=2*b) expression is defined as an integer column.
[4 Jun 2020 8:47] MySQL Verification Team
Hello Nikolai,

Thank you for the report and feedback.
Verified as described with 8.0.20 build.

Thanks,
Umesh
[25 Aug 2020 20:30] Trey Raymond
some add'l context

when a condition is evaluated, it's evaluated as IS TRUE.  like "where a=b" is the same as "where (a=b) is true"

"is true" as an operator is handled ~as "!=0" - a condition which the optimizer doesn't generally like to use an index for.  but in this case, it's really only possible to be 0 or 1 as the condition is evaluated.

It may be complicated to make this smart in a way that doesn't break non-boolean results, but it'd be a great tool if that can be done.