给定:
CREATE TABLE `APPLICATION_DEVICE_PUSHINFO` (
`applicationId` bigint(20) NOT NULL,
`deviceId` bigint(20) NOT NULL,
`active` bit(1) NOT NULL,
`inactiveAsOf` datetime DEFAULT NULL,
`lastSentOn` datetime DEFAULT NULL,
`registeredOn` datetime DEFAULT NULL,
`target` int(11) DEFAULT NULL,
`token` varchar(4096) NOT NULL,
PRIMARY KEY (`applicationId`,`deviceId`),
KEY `FKE7F2D58285EFFEAA_idx` (`deviceId`),
KEY `index3` (`token`(255)) USING BTREE,
CONSTRAINT `FKE7F2D58285EFFEAA` FOREIGN KEY (`deviceId`) REFERENCES `DEVICES` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
如果我执行以下查询:
explain SELECT token FROM APPLICATION_DEVICE_PUSHINFO group by token having count(deviceId) > 1;;
我得到:
'1', 'SIMPLE', 'APPLICATION_DEVICE_PUSHINFO', 'ALL', NULL, NULL, NULL, NULL, '7', 'Using temporary; Using filesort'
空值属于可能的键等。
为什么不使用列标记的索引?
最佳答案
由于您没有 WHERE 子句,查询需要处理所有行(注意 HAVING 子句在 GROUP BY 之后应用——因此它不限制要处理的行,只限制返回的行).
如果您无论如何都需要接触所有行,则很难从索引中获得任何好处。不过,如果您能够执行仅索引扫描 (IOS) 和/或从磁盘上的预购数据中获益,则有可能有所收获。
但是,IOS 可能(不确定 MySQL 是否考虑了 NOT NULL 约束)由于您访问 deviceId
列而被阻止,该列不包含在可能用于此的索引中查询(index3
)。请注意,您需要拥有一个涵盖查询所有需求的索引才能获得仅索引扫描。然而,如果 MySQL 足够聪明并且识别 NOT NULL 约束,这应该不是问题。否则,重写您的查询。例如计数(*) > 1。
在这种特殊情况下,由于表的尺寸较小(至少根据优化器估计)(正如 Strawberry 已经提到的那样),您无论如何都有可能获得同样糟糕的 IOS。
如果您需要确保它也适用于更多行,只需填写表格并查看它是否更改了执行计划。如果不是,请按上述更改查询,然后重试。如果没有,请返回此处,我们会看到(发布新的执行计划)。
您希望通过索引执行此查询原则上是合理的。让它工作是另一回事:(
关于Mysql分组不使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20148997/