MySQL TINYINT 列上的索引:为什么检查“= 0”和检查 'is false' 之间存在差异?
MySQL index on TINYINT column: why is there a difference between checking '= 0' and checking 'is false'?
MySQL 为查询 提供不同的执行计划,具体取决于我检查的是索引 TINYINT 字段 'is false' 还是 '= 0'。 table 的名称是 ordini(意思是 'shipments'),我希望 MySQL 使用的索引是 shipmentslistrequest
SHOW CREATE ordini
(为简单起见,我省略了大部分列和索引):
CREATE TABLE `ordini` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dataIns` datetime DEFAULT NULL,
`hasLDV` tinyint(1) NOT NULL DEFAULT '0',
`isAnnullato` tinyint(1) NOT NULL DEFAULT '0',
`isEsportatoSAM` tinyint(1) DEFAULT '0',
`id_azienda_aux` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `dataIns` (`dataIns`),
KEY `id_azienda_aux` (`id_azienda_aux`),
KEY `shipmentslistrequest` (`id_azienda_aux`,`isEsportatoSAM`,
`hasLDV`,`isAnnullato`,`dataIns`)
) ENGINE=InnoDB AUTO_INCREMENT=5007359 DEFAULT CHARSET=latin1
查询 1:
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM = 0
and isAnnullato = 0
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type |possible_keys |key |key_len|ref|rows|Extra |
--|-----------|------|-----|-------------------------------------------|--------------------|-------|---|----|-----------|
1|SIMPLE |ordini|range|dataIns,id_azienda_aux,shipmentslistrequest|shipmentslistrequest|17 | | 138|Using where|
Query2(更改:isEsportatoSAM 为假):
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM is false
and isAnnullato = 0
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type |possible_keys |key |key_len|ref|rows |Extra |
--|-----------|------|-----|-------------------------------------------|-------|-------|---|------|-----------|
1|SIMPLE |ordini|range|dataIns,id_azienda_aux,shipmentslistrequest|dataIns|9 | |205920|Using where|
Query3(更改:isEsportatoSAM = 0 并且 isAnnullato 为 false):
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM = 0
and isAnnullato is false
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra |
--|-----------|------|----|-------------------------------------------|--------------------|-------|-----------------|----|-----------|
1|SIMPLE |ordini|ref |dataIns,id_azienda_aux,shipmentslistrequest|shipmentslistrequest|7 |const,const,const| 206|Using where|
我的猜测是:
Query1 将正确利用 shipmentslistrequest 索引。
Query2 无法在第一列之外使用它,因为我将 isEsportatoSAM 与 false 进行比较(但为什么呢?。事实是否如此?该列承认空值有任何相关性?),因此选择了不同的索引。
Query3 将使用索引直到(并包括)第三列,但由于 'is false' 比较而不再使用,这就是为什么三个 'const' 在 ref 列中,这就是为什么连接类型是 'ref' 而不是 'range' (它没有到达索引的日期范围部分)。 这个解释正确吗?
谁能帮我解决这些问题?
PS: MySQL版本为5.5.56
PS2:从设计的角度来看,我知道在那些 tinyint 列中允许空值是没有意义的(我没有设计这个系统)。
这只是一个猜测,但我怀疑这是因为 IS boolean_value
旨在在测试之前将任何数据类型转换为其布尔值。查询优化器 应该 能够在列是任何类型的 INT
时判断 IS FALSE
等同于 = 0
,然后使用索引与第一个查询一样,但显然 MySQL 没有实现该优化。
如果您想要出色的查询优化,MySQL 通常不是首选的数据库。
isAnnullato = 0
让优化器使用索引。
isAnnullato is false
要求查询查看每一行并在布尔上下文中计算 isAnnullato
。
MySQL 为查询 提供不同的执行计划,具体取决于我检查的是索引 TINYINT 字段 'is false' 还是 '= 0'。 table 的名称是 ordini(意思是 'shipments'),我希望 MySQL 使用的索引是 shipmentslistrequest
SHOW CREATE ordini
(为简单起见,我省略了大部分列和索引):
CREATE TABLE `ordini` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dataIns` datetime DEFAULT NULL,
`hasLDV` tinyint(1) NOT NULL DEFAULT '0',
`isAnnullato` tinyint(1) NOT NULL DEFAULT '0',
`isEsportatoSAM` tinyint(1) DEFAULT '0',
`id_azienda_aux` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `dataIns` (`dataIns`),
KEY `id_azienda_aux` (`id_azienda_aux`),
KEY `shipmentslistrequest` (`id_azienda_aux`,`isEsportatoSAM`,
`hasLDV`,`isAnnullato`,`dataIns`)
) ENGINE=InnoDB AUTO_INCREMENT=5007359 DEFAULT CHARSET=latin1
查询 1:
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM = 0
and isAnnullato = 0
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type |possible_keys |key |key_len|ref|rows|Extra |
--|-----------|------|-----|-------------------------------------------|--------------------|-------|---|----|-----------|
1|SIMPLE |ordini|range|dataIns,id_azienda_aux,shipmentslistrequest|shipmentslistrequest|17 | | 138|Using where|
Query2(更改:isEsportatoSAM 为假):
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM is false
and isAnnullato = 0
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type |possible_keys |key |key_len|ref|rows |Extra |
--|-----------|------|-----|-------------------------------------------|-------|-------|---|------|-----------|
1|SIMPLE |ordini|range|dataIns,id_azienda_aux,shipmentslistrequest|dataIns|9 | |205920|Using where|
Query3(更改:isEsportatoSAM = 0 并且 isAnnullato 为 false):
EXPLAIN select *
from ordini
where id_azienda_aux = 92
and isEsportatoSAM = 0
and isAnnullato is false
and hasLDV = 1
and dataIns >= '2020-04-28'
and dataIns < '2020-05-19';
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra |
--|-----------|------|----|-------------------------------------------|--------------------|-------|-----------------|----|-----------|
1|SIMPLE |ordini|ref |dataIns,id_azienda_aux,shipmentslistrequest|shipmentslistrequest|7 |const,const,const| 206|Using where|
我的猜测是:
Query1 将正确利用 shipmentslistrequest 索引。
Query2 无法在第一列之外使用它,因为我将 isEsportatoSAM 与 false 进行比较(但为什么呢?。事实是否如此?该列承认空值有任何相关性?),因此选择了不同的索引。
Query3 将使用索引直到(并包括)第三列,但由于 'is false' 比较而不再使用,这就是为什么三个 'const' 在 ref 列中,这就是为什么连接类型是 'ref' 而不是 'range' (它没有到达索引的日期范围部分)。 这个解释正确吗?
谁能帮我解决这些问题?
PS: MySQL版本为5.5.56
PS2:从设计的角度来看,我知道在那些 tinyint 列中允许空值是没有意义的(我没有设计这个系统)。
这只是一个猜测,但我怀疑这是因为 IS boolean_value
旨在在测试之前将任何数据类型转换为其布尔值。查询优化器 应该 能够在列是任何类型的 INT
时判断 IS FALSE
等同于 = 0
,然后使用索引与第一个查询一样,但显然 MySQL 没有实现该优化。
如果您想要出色的查询优化,MySQL 通常不是首选的数据库。
isAnnullato = 0
让优化器使用索引。
isAnnullato is false
要求查询查看每一行并在布尔上下文中计算 isAnnullato
。