Description:
MySQL does not use the functional index on the function LOG10
How to repeat:
mysql> CREATE TABLE expdata ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> value FLOAT,
-> INDEX log10_fi ((log10(value)))
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO expdata (value) VALUES (.01),(.1),(1),(10),(100);
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT id, value, log10(value) FROM expdata;
+----+-------+--------------------+
| id | value | log10(value) |
+----+-------+--------------------+
| 1 | 0.01 | -2.000000009707238 |
| 2 | 0.1 | -0.999999993528508 |
| 3 | 1 | 0 |
| 4 | 10 | 1 |
| 5 | 100 | 2 |
+----+-------+--------------------+
5 rows in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM expdata WHERE log10(value) < 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | expdata | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Note (Code 1003): /* select#1 */ select `cookbook`.`expdata`.`id` AS `id`,`cookbook`.`expdata`.`value` AS `value` from `cookbook`.`expdata` where (log10(`cookbook`.`expdata`.`value`) < 0)
-- OK, maybe it complains about range? What about equality?
mysql> EXPLAIN SELECT * FROM expdata WHERE log10(value) = 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | expdata | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Note (Code 1003): /* select#1 */ select `cookbook`.`expdata`.`id` AS `id`,`cookbook`.`expdata`.`value` AS `value` from `cookbook`.`expdata` where (log10(`cookbook`.`expdata`.`value`) = 0)
-- Maybe the reason is that our column defined as FLOAT that makes any function not deterministic?
-- Let's try with DECIMAL for which LOG10 will always generate deterministic values
mysql> DROP TABLE expdata;
Query OK, 0 rows affected (0.02 sec)
mysql> CREATE TABLE expdata ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> value DECIMAL (10, 2),
-> INDEX log10_fi ((log10(value)))
-> );
Query OK, 0 rows affected (0.03 sec)
mysql>
mysql> INSERT INTO expdata (value) VALUES (.01),(.1),(1),(10),(100);
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT id, value, log10(value) FROM expdata;
+----+--------+--------------+
| id | value | log10(value) |
+----+--------+--------------+
| 1 | 0.01 | -2 |
| 2 | 0.10 | -1 |
| 3 | 1.00 | 0 |
| 4 | 10.00 | 1 |
| 5 | 100.00 | 2 |
+----+--------+--------------+
5 rows in set (0.00 sec)
mysql>
mysql> EXPLAIN SELECT * FROM expdata WHERE log10(value) < 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | expdata | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Note (Code 1003): /* select#1 */ select `cookbook`.`expdata`.`id` AS `id`,`cookbook`.`expdata`.`value` AS `value` from `cookbook`.`expdata` where (log10(`cookbook`.`expdata`.`value`) < 0)
mysql> EXPLAIN SELECT * FROM expdata WHERE log10(value) = 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | expdata | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Note (Code 1003): /* select#1 */ select `cookbook`.`expdata`.`id` AS `id`,`cookbook`.`expdata`.`value` AS `value` from `cookbook`.`expdata` where (log10(`cookbook`.`expdata`.`value`) = 0)
-- Still no luck!
-- What about PostgreSQL?
postgres=# create table expdata(id int, value float);
CREATE TABLE
postgres=# CREATE INDEX test_log10_fi on expdata (log10(value));
CREATE INDEX
postgres=# insert into expdata values(1,.01),(2,.1),(3,1),(4,10),(5,100);
INSERT 0 5
postgres=# explain SELECT * FROM expdata WHERE log10(value) < 0;
QUERY PLAN
------------------------------------------------------------------------------
Bitmap Heap Scan on expdata (cost=9.42..29.62 rows=680 width=12)
Recheck Cond: (log10(value) < '0'::double precision)
-> Bitmap Index Scan on test_log10_fi (cost=0.00..9.25 rows=680 width=0)
Index Cond: (log10(value) < '0'::double precision)
(4 rows)
postgres=# explain SELECT * FROM expdata WHERE log10(value) = 0;
QUERY PLAN
-----------------------------------------------------------------------------
Bitmap Heap Scan on expdata (cost=4.23..14.81 rows=10 width=12)
Recheck Cond: (log10(value) = '0'::double precision)
-> Bitmap Index Scan on test_log10_fi (cost=0.00..4.23 rows=10 width=0)
Index Cond: (log10(value) = '0'::double precision)
(4 rows)
It perfectly works!
Suggested fix:
Use functional indexes on LOG10. Probably test all built-in deterministic functions?