1

MySQL 为查询提供不同的执行计划,具体取决于我检查索引 TINYINT 字段是 'is false' 还是 '= 0'。该表的名称是ordini (意思是' shipments '),我希望 MySQL 使用的索引是 shippinglistrequest

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 为 false):

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将正确利用 shippinglistrequest 索引。

Query2不能在第一列之外使用它,因为我将 isEsportatoSAM 与 false 进行比较(但为什么?该列承认空值的事实是否有任何相关性?),因此选择了不同的索引。

Query3将使用索引直到(并包括)第三列,但由于“为假”比较而不再使用,这就是 ref 列中三个“const”的原因,这就是连接类型为“ref”的原因而不是“范围”(它没有到达索引的日期范围部分)。这种解释正确吗?

谁能帮我解决这些问题?

PS:MySQL版本为5.5.56

PS2:从设计的角度来看,我知道在这些 tinyint 列中允许空值是没有意义的(我没有设计这个系统)。

4

2 回答 2

1

这只是一个猜测,但我怀疑这是因为IS boolean_value设计用于在测试之前将任何数据类型转换为其布尔值。查询优化器应该能够判断出该列IS FALSE何时= 0是任何类型的INT,然后在第一个查询中使用索引,但显然 MySQL 没有实现该优化。

如果您想要出色的查询优化,MySQL 通常不是首选的数据库。

于 2020-05-18T21:03:03.677 回答
1

isAnnullato = 0让优化器使用索引。

isAnnullato is false要求查询查看每一行并isAnnullato在布尔上下文中进行评估。

于 2020-05-18T21:07:00.167 回答