我有一个SQL查询,它在表中查找特定值,然后跨三个表进行内部联接以获取结果集.这三张桌子分别是fabric_barcode_ocfabric_barcode_items和;fabric_barcode_rolls

Initial Query

下面是查询的初始版本

EXPLAIN ANALYZE
SELECT `oc`.`oc_number` AS `ocNumber` , `roll`.`po_number` AS `poNumber` ,
`item`.`item_code` AS `itemCode` , `roll`.`roll_length` AS `rollLength` ,
`roll`.`roll_utilized` AS `rollUtilized`
FROM `fabric_barcode_rolls` AS `roll`
INNER JOIN `fabric_barcode_oc` AS `oc` ON `oc`.`oc_unique_id` = `roll`.`oc_unique_id`
INNER JOIN `fabric_barcode_items` AS `item` ON `item`.`item_unique_id` = `roll`.`item_unique_id_fk`
WHERE BINARY `roll`.`roll_number` = 'dZkzHJ_je8'

当我在这台机器上运行EXPLAIN ANALYZE次时,我得到以下结果

"-> Nested loop inner join  (cost=468160.85 rows=582047) (actual time=0.063..254.186 rows=1 loops=1)
    -> Nested loop inner join  (cost=264444.40 rows=582047) (actual time=0.057..254.179 rows=1 loops=1)
        -> Filter: (cast(roll.roll_number as char charset binary) = 'dZkzHJ_je8')  (cost=60727.95 rows=582047) (actual time=0.047..254.169 rows=1 loops=1)
            -> Table scan on roll  (cost=60727.95 rows=582047) (actual time=0.042..198.634 rows=599578 loops=1)
        -> Single-row index lookup on oc using PRIMARY (oc_unique_id=roll.oc_unique_id)  (cost=0.25 rows=1) (actual time=0.009..0.009 rows=1 loops=1)
    -> Single-row index lookup on item using PRIMARY (item_unique_id=roll.item_unique_id_fk)  (cost=0.25 rows=1) (actual time=0.006..0.006 rows=1 loops=1)
"

Updated Query

然后我将查询改为

EXPLAIN ANALYZE
SELECT `oc`.`oc_number` AS `ocNumber` , `roll`.`po_number` AS `poNumber` ,
`item`.`item_code` AS `itemCode` , `roll`.`roll_length` AS `rollLength` ,
`roll`.`roll_utilized` AS `rollUtilized`
FROM `fabric_barcode_rolls` AS `roll`
INNER JOIN `fabric_barcode_oc` AS `oc` ON `oc`.`oc_unique_id` = `roll`.`oc_unique_id`
INNER JOIN `fabric_barcode_items` AS `item` ON `item`.`item_unique_id` = `roll`.`item_unique_id_fk`
WHERE `roll`.`roll_number` = 'dZkzHJ_je8'

这将生成以下执行计划

"-> Rows fetched before execution  (cost=0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)

这两个查询之间的唯一区别是,我从查询中删除了BINARY函数调用.我不明白为什么这个计划如此不同?

Execution Times

查询1的执行时间约为375ms,而第二个查询的执行时间约为160ms.

造成这种差异的原因是什么?

UPDATE

根据要求包括fabric_barcode_rolls的表模式定义

fabric_barcode_rolls,"CREATE TABLE `fabric_barcode_rolls` (
  `roll_unique_id` int NOT NULL AUTO_INCREMENT,
  `oc_unique_id` int NOT NULL,
  `item_unique_id_fk` int NOT NULL,
  `roll_number` char(30) NOT NULL,
  `roll_length` decimal(10,2) DEFAULT '0.00',
  `po_number` char(22) DEFAULT NULL,
  `roll_utilized` decimal(10,2) DEFAULT '0.00',
  `user` char(30) NOT NULL,
  `mir_number` char(22) DEFAULT NULL,
  `mir_location` char(10) DEFAULT NULL,
  `mir_stamp` datetime DEFAULT NULL,
  `creation_stamp` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_stamp` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`roll_unique_id`),
  UNIQUE KEY `roll_number` (`roll_number`),
  KEY `fabric_barcode_item_fk` (`item_unique_id_fk`),
  CONSTRAINT `fabric_barcode_item_fk` FOREIGN KEY (`item_unique_id_fk`) REFERENCES `fabric_barcode_items` (`item_unique_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=610684 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci"

推荐答案

性能差异是由以下事实造成的:在MySQL中,VARCHAR()和CHAR()列的排序规则被烘焙到索引中.

Edit已更新以匹配表定义.

您的fabric_barcode_rolls表有一个定义如下的列:

roll_number char(30) NOT NULL,
...
UNIQUE KEY roll_number (roll_number).

所以,您的WHERE ... BINARY roll.roll_number = 'dZkzHJ_je8' filter子句是not sargable:它不能在该列上使用索引.但WHERE ... roll.roll_number = 'dZkzHJ_je8'是可搜索的:它确实使用索引.所以很快.但该列的默认排序规则不区分大小写.所以,这很快就错了.

这是可以解决的.

请注意,该列上没有排序规则声明.这意味着它使用的是表的默认值:utf8mb4_0900_ai_ci,不区分大小写的排序规则.

普通条形码列需要的是每个字符一个字节的字符集和区分大小写的排序规则.这会改变你的桌子来做到这一点.

 ALTER TABLE fabric_barcode_rolls
CHANGE  roll_number 
        roll_number CHAR(30) COLLATE latin1_bin NOT NULL;

这是一场多层次的胜利.为条形码使用正确的字符集可以保存数据.它使索引更短,使用效率更高.它进行区分大小写(二进制匹配)的查找,这本身使索引更短,使用效率更高.而且它不会在带有大小写字符集的条形码之间产生冲突风险.

在你得出结论认为碰撞风险很低,你不必担心之前,请阅读关于生日悖论的文章.

Mysql相关问答推荐

MySQL::JSON列的添加使SELECT查询非性能(当需要临时表时)

对匹配两个或多个表的表结果进行排序

按唯一列排序,但保持匹配的列在一起

生成json数据并根据特定表的匹配条件进行过滤

关于设置为 NOT NULL 时的 CHAR 默认值

如何将数据导入MySQL中的master/replica struct

如何在 MySQL 中使用从 SELECT IF 返回的布尔值

错误1075:表定义不正确;只能有一个自动列,它必须定义为一个键(使用 gorm 和 mysql)

为什么以及如何将 Django filter-by BooleanField 查询转换为 SQL WHERE 或 WHERE NOT 而不是 1/0?

如何使用 sequelize 将复合主键放在连接表中?

函数 mysql_real_escape_string 的 PDO 类似功能是什么?

MYSQL:创建表中的索引关键字以及何时使用它

MySQL - 使一对值唯一

有什么方法可以显示`gunzip < database.sql.gz | 的进度? mysql ...`进程?

重新加载 .env 变量而不重新启动服务器(Laravel 5,共享主机)

PHP 判断 NULL

MySQL Partitioning / Sharding / Splitting - 走哪条路?

在 MySQL 中签名或未签名

在 MySQL 中仅 Select 仅包含字母数字字符的行

如何在执行 sql 脚本时 echo 打印语句