Mysql 在查询tinyint bool 字段时的奇怪行为

标签 mysql

我有一个 mysql 表 voucher_codes ,其架构如下:

mysql> describe  voucher_codes;
+--------------------+------------------+------+-----+---------+----------------+
| Field              | Type             | Null | Key | Default | Extra          |
+--------------------+------------------+------+-----+---------+----------------+
| id                 | int(11)          | NO   | PRI | NULL    | auto_increment |
| user_id            | int(10) unsigned | YES  | MUL | NULL    |                |
| promo_code         | varchar(30)      | YES  | MUL | NULL    |                |
| voucher_value      | double           | NO   |     | NULL    |                |
| date_created       | datetime(6)      | NO   |     | NULL    |                |
| date_modified      | datetime(6)      | NO   |     | NULL    |                |
| redeem_flag        | tinyint(1)       | NO   |     | NULL    |                |
| fk_voucher_rule_id | int(11)          | NO   | MUL | NULL    |                |
+--------------------+------------------+------+-----+---------+----------------+

表上定义的索引如下:

mysql> show index  from voucher_codes;
+---------------+------------+--------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table         | Non_unique | Key_name                 | Seq_in_index | Column_name        | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------------+------------+--------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| voucher_codes |          0 | PRIMARY                  |            1 | id                 | A         |    15280692 |     NULL | NULL   |      | BTREE      |         |               |
| voucher_codes |          1 | voucher_codes_user_id    |            1 | user_id            | A         |     2805369 |     NULL | NULL   | YES  | BTREE      |         |               |
| voucher_codes |          1 | voucher_codes_promo_code |            1 | promo_code         | A         |     7389780 |     NULL | NULL   | YES  | BTREE      |         |               |
| voucher_codes |          1 | fk_voucher_rule_id       |            1 | fk_voucher_rule_id | A         |          60 |     NULL | NULL   |      | BTREE      |         |               |
+---------------+------------+--------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

voucher_code为1、redeem_flag分别为0和1的记录条数如下:

mysql> select count(*) from voucher_codes  where redeem_flag =0 and fk_voucher_rule_id=1;
+----------+
| count(*) |
+----------+
|   135114 |
+----------+
1 row in set (3.17 sec)

mysql> select count(*) from voucher_codes  where redeem_flag =1 and fk_voucher_rule_id=1;
+----------+
| count(*) |
+----------+
|  1575024 |
+----------+
1 row in set (2.56 sec)

redeem_flag1 的记录超过 150 万条,0 的记录超过 10 万条。

但与redeem_flag为0的查询相比,redeem_flag1的查询花费的时间更少。 以下是结果:

mysql> select promo_code from voucher_codes  where redeem_flag =0 and fk_voucher_rule_id=1  limit 1 ;
+--------------+
| promo_code   |
+--------------+
| XXXXXXXXXX   |
+--------------+
1 row in set (3.67 sec)

mysql> explain select promo_code from voucher_codes  where redeem_flag =0 and fk_voucher_rule_id=1  limit 1 ;
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys      | key                | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | voucher_codes | NULL       | ref  | fk_voucher_rule_id | fk_voucher_rule_id | 4       | const | 3258352 |    10.00 | Using where |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

redeem_flag为1的查询:

mysql> select promo_code from voucher_codes  where redeem_flag =1 and fk_voucher_rule_id=1  limit 1 ;
+------------+
| promo_code |
+------------+
| XXXXXXXXX  |
+------------+
1 row in set (0.00 sec)

mysql> explain select promo_code from voucher_codes  where redeem_flag =1 and fk_voucher_rule_id=1  limit 1 ;
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys      | key                | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | voucher_codes | NULL       | ref  | fk_voucher_rule_id | fk_voucher_rule_id | 4       | const | 3258352 |    10.00 | Using where |
+----+-------------+---------------+------------+------+--------------------+--------------------+---------+-------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

无法理解这种行为。尽管索引 fk_voucher_rule_id 已被使用,但与记录数量较少的记录相比,记录数量较多的记录需要更多时间。

更新:添加显示创建表结果:

mysql> show create table voucher_codes;
+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table         | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| voucher_codes | CREATE TABLE `voucher_codes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned DEFAULT NULL,
  `promo_code` varchar(30) DEFAULT NULL,
  `voucher_value` double NOT NULL,
  `date_created` datetime(6) NOT NULL,
  `date_modified` datetime(6) NOT NULL,
  `redeem_flag` tinyint(1) NOT NULL,
  `fk_voucher_rule_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `voucher_codes_user_id` (`user_id`),
  KEY `voucher_codes_promo_code` (`promo_code`),
  KEY `fk_voucher_rule_id` (`fk_voucher_rule_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16305657 DEFAULT CHARSET=latin1 |
+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

最佳答案

首先,使用 SELECT SQL_NO_CACHE ... 而不仅仅是 SELECT ... 测试查询速度。查询运行时间为 0.00 很奇怪。

此外,在 redeem_flag、fk_voucher_rule 上创建复合索引,然后重新测试您的查询以进行比较。

注意:您应该在查询中使用与索引中相同的字段顺序。另外,正如@Raymod Nijland 所说,删除冗余索引。例如,如果您有索引fk_voucher_rule,redeem_flag,它将同时用于仅使用fk_voucher_rule的查询和fk_voucher_rule,redeem_flag查询(其他方式around 不适用,您不能仅将其用于查询 redeem_flag)。

关于Mysql 在查询tinyint bool 字段时的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58503172/

相关文章:

SQL查询中的PHP if语句

php - 如何返回数组中所有值都存在的行

c# - 如何在 datagridview 中插入新的 datagridview 行并在数据库中(自动)更新?

php - Laravel 5.1( Eloquent )在使用关系附加或分离时不起作用

MySQL count of count,将一个表的结果与另一个表一起使用

mysql - 更改 SQL 数据库服务器

mysql - 选择查询中单个 IF 中的多个条件

mysql - SQL 查询 - 两个多对一关系的表(规范化问题)

mysql - Wordpress:将主题恢复为默认设置

java - 在Netbeans上在线发布用Java开发的网页