Bug #115410 MySQL bit field index affects query results unexpectedly.
Submitted: 23 Jun 16:02 Modified: 25 Jun 10:06
Reporter: yonghua Lin Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Data Types Severity:S3 (Non-critical)
Version:8.0 OS:Any
Assigned to: CPU Architecture:Any

[23 Jun 16:02] yonghua Lin
Description:
After adding an index to a bit field, the query results differ from when the index is not present. This issue was observed in a table where a bit field was indexed, and a query that previously returned multiple rows , BUT did return no rows with the index in place. This behavior is unexpected, as indexes should not alter the query results.

How to repeat:

MySQL 8.0.35 Community Edition.

CREATE TABLE `student_attend` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '自增编号',
  `std_id` int DEFAULT NULL COMMENT '学号',
  `class_id` int DEFAULT NULL COMMENT '课程编号',
  `is_attend` bit(1) DEFAULT b'1' COMMENT '是否缺陷考勤',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

insert into student_attend(std_id, class_id, is_attend)
select 1001, 1, 1 from dual union all
select 1001, 2, 0 from dual union all
select 1001, 3, 1 from dual union all
select 1001, 4, 1 from dual union all
select 1001, 5, 1 from dual union all
select 1001, 6, 0 from dual union all
select 1002, 1, 1 from dual union all
select 1002, 2, 1 from dual union all
select 1003, 1, 0 from dual union all
select 1003, 2, 0 from dual;

mysql> select * from student_attend where is_attend=1;
+----+--------+----------+----------------------+
| id | std_id | class_id | is_attend            |
+----+--------+----------+----------------------+
|  1 |   1001 |        1 | 0x01                 |
|  3 |   1001 |        3 | 0x01                 |
|  4 |   1001 |        4 | 0x01                 |
|  5 |   1001 |        5 | 0x01                 |
|  7 |   1002 |        1 | 0x01                 |
|  8 |   1002 |        2 | 0x01                 |
+----+--------+----------+----------------------+
6 rows in set (0.00 sec)

mysql> select * from student_attend where is_attend=b'1';
+----+--------+----------+----------------------+
| id | std_id | class_id | is_attend            |
+----+--------+----------+----------------------+
|  1 |   1001 |        1 | 0x01                 |
|  3 |   1001 |        3 | 0x01                 |
|  4 |   1001 |        4 | 0x01                 |
|  5 |   1001 |        5 | 0x01                 |
|  7 |   1002 |        1 | 0x01                 |
|  8 |   1002 |        2 | 0x01                 |
+----+--------+----------+----------------------+
6 rows in set (0.00 sec)

mysql> select * from student_attend where is_attend='1';
+----+--------+----------+----------------------+
| id | std_id | class_id | is_attend |
+----+--------+----------+----------------------+
| 1 | 1001 | 1 | 0x01 |
| 3 | 1001 | 3 | 0x01 |
| 4 | 1001 | 4 | 0x01 |
| 5 | 1001 | 5 | 0x01 |
| 7 | 1002 | 1 | 0x01 |
| 8 | 1002 | 2 | 0x01 |
+----+--------+----------+----------------------+
6 rows in set (0.00 sec)

Next, we create an index ix_student_attend_n1 on the field is_attend, Then we continue to test and verify, and the situation I mentioned above will appear.
As shown, the last SQL returns 0 records.

create index ix_student_attend_n1 on student_attend(is_attend);

mysql> select * from student_attend where is_attend=1;
+----+--------+----------+----------------------+
| id | std_id | class_id | is_attend |
+----+--------+----------+----------------------+
| 1 | 1001 | 1 | 0x01 |
| 3 | 1001 | 3 | 0x01 |
| 4 | 1001 | 4 | 0x01 |
| 5 | 1001 | 5 | 0x01 |
| 7 | 1002 | 1 | 0x01 |
| 8 | 1002 | 2 | 0x01 |
+----+--------+----------+----------------------+
6 rows in set (0.00 sec)

mysql> select * from student_attend where is_attend=b'1';
+----+--------+----------+----------------------+
| id | std_id | class_id | is_attend |
+----+--------+----------+----------------------+
| 1 | 1001 | 1 | 0x01 |
| 3 | 1001 | 3 | 0x01 |
| 4 | 1001 | 4 | 0x01 |
| 5 | 1001 | 5 | 0x01 |
| 7 | 1002 | 1 | 0x01 |
| 8 | 1002 | 2 | 0x01 |
+----+--------+----------+----------------------+
6 rows in set (0.00 sec)

mysql> select * from student_attend where is_attend='1';
Empty set (0.00 sec)

The execution plan only has the prompt "message": "no matching row in const table"

mysql> explain
-> select * from student_attend where is_attend='1';
+----+-------------+-------+---------------+------+---------+------+------+----------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+-------------+------+---------+------+------+------+------+--------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
1 row in set, 1 warning (0.00 sec)

Suggested fix:

This problem is very confusing. We can only rewrite the SQL statement to avoid this bit type field problem

select * from student_attend where is_attend=b'1';
or
select * from student_attend where is_attend=1;
[23 Jun 16:06] yonghua Lin
thank you
[25 Jun 10:06] MySQL Verification Team
Hi Mr. Lin,

Thank you very much for your bug report.

We were able to repeat it with our official binaries for 8.0.37 and 8.4.0:

id	std_id	class_id	is_attend
1	1001	1
3	1001	3
4	1001	4
5	1001	5
7	1002	1
8	1002	2
id	std_id	class_id	is_attend
1	1001	1
3	1001	3
4	1001	4
5	1001	5
7	1002	1
8	1002	2
---------------------------
id	std_id	class_id	is_attend
1	1001	1
3	1001	3
4	1001	4
5	1001	5
7	1002	1
8	1002	2
id	std_id	class_id	is_attend
1	1001	1
3	1001	3
4	1001	4
5	1001	5
7	1002	1
8	1002	2
id	std_id	class_id	is_attend
1	1001	1
3	1001	3
4	1001	4
5	1001	5
7	1002	1
8	1002	2
---------------------------

Verified as reported.