【发布时间】:2014-01-22 09:07:38
【问题描述】:
所以我有一个表,我在其中填充了大约 700K 条目进行测试,这表明它在 MySQL 查询中很痛苦。
有问题的表:
CREATE TABLE `trades` (
`tradeId` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) NOT NULL,
`visible` int(11) NOT NULL DEFAULT '1',
`sourceItem` int(11) NOT NULL,
`sourceKeyTierId` int(11) DEFAULT NULL,
`sourceKeyTypeId` int(11) DEFAULT NULL,
`sourceKeyAmount` int(11) DEFAULT NULL,
`sourceModId` int(11) DEFAULT NULL,
`sourceModLevel` int(11) DEFAULT NULL,
`destinationItem` int(11) NOT NULL,
`destinationPlatinum` int(11) DEFAULT NULL,
`destinationKeyTierId` int(11) DEFAULT NULL,
`destinationKeyTypeId` int(11) DEFAULT NULL,
`destinationKeyAmount` int(11) DEFAULT NULL,
`destinationModId` int(11) DEFAULT NULL,
`destinationModLevel` int(11) DEFAULT NULL,
`added` datetime NOT NULL,
PRIMARY KEY (`tradeId`),
KEY `userId` (`userId`),
KEY `sourceKeyTierId` (`sourceKeyTierId`),
KEY `sourceKeyTypeId` (`sourceKeyTypeId`),
KEY `sourceModId` (`sourceModId`),
KEY `destinationKeyTierId` (`destinationKeyTierId`),
KEY `destinationKeyTypeId` (`destinationKeyTypeId`),
KEY `destinationModId` (`destinationModId`),
CONSTRAINT `trades_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `users` (`userId`),
CONSTRAINT `trades_ibfk_2` FOREIGN KEY (`sourceKeyTierId`) REFERENCES `keytiers` (`keyTierId`),
CONSTRAINT `trades_ibfk_3` FOREIGN KEY (`sourceKeyTypeId`) REFERENCES `keytypes` (`keyTypeId`),
CONSTRAINT `trades_ibfk_4` FOREIGN KEY (`sourceModId`) REFERENCES `mods` (`modId`),
CONSTRAINT `trades_ibfk_5` FOREIGN KEY (`destinationKeyTierId`) REFERENCES `keytiers` (`keyTierId`),
CONSTRAINT `trades_ibfk_6` FOREIGN KEY (`destinationKeyTypeId`) REFERENCES `keytypes` (`keyTypeId`),
CONSTRAINT `trades_ibfk_7` FOREIGN KEY (`destinationModId`) REFERENCES `mods` (`modId`)
) ENGINE=InnoDB AUTO_INCREMENT=732544 DEFAULT CHARSET=latin1
现在在获取结果集时,我想计算结果的数量,以决定是否显示未找到结果的消息。
SELECT SUM(count) AS sum
FROM
(
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 1)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 2)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 3)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 1)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 2)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 3)
) AS derived
查询正在运行,但需要 2.63 秒,这太长了。
如何优化这个?我以为我几乎已经完成了我能做的所有事情,除了一件事:
- 由于
sourceItem的可能值为(1, 2),destinationItem的可能值为(1, 2, 3),我可以创建另一个表并通过TRIGGER ON INSERT写入包含这些值的表。
同样重要的是,查询是由依赖于 POST 变量的 PHP 脚本创建的,这意味着 UNION ALL 中的每个 SELECT 可能存在也可能不存在。不幸的是,这个问题并不像返回全表的最大值那么简单。
也欢迎所有其他建议。
更新: 显然查询的实际构造方式存在一些混淆,如下所示:
-
sourceItem有 2 个复选框,对应于1和2 -
destinationItem有 3 个复选框,对应于1、2和3
用户可以以任何他想要的方式检查它们。
更新 2: 似乎我的原始查询不会削减它,即使使用索引,有人可以这么好心地考虑一个完全不同的设置,基本上消除了对 COUNT 的需要或SUM 或类似的东西?
更新 3:我忘记了我的问题中一个非常重要的部分,如下:
- 拥有
sourceItem = 1可能有(sourceKeyTierId = ?ANDsourceKeyTypeId= ?)与之关联 - 拥有
sourceItem = 2可能有(sourceModId = ?)与之关联 - 拥有
destinationItem = 2可能有(destinationKeyTierId = ?ANDdestinationKeyTypeId= ?)与之关联 - 拥有
destinationItem = 3可能有(destinationModId = ?)与之关联
您仍然可以在带有复选框的示例中看到它,但是选中某些复选框也会导致输入另一个数字的选项(在实际场景中它是一个选择下拉菜单),不需要选择。
我的第一个未优化查询的更新示例是以下查询可能也会发生,但这是最大的示例:
SELECT SUM(count) AS sum
FROM
(
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 1 AND t.sourceKeyTierId = ? AND t.sourceKeyTypeId = ?)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 2 AND t.sourceKeyTierId = ? AND t.sourceKeyTypeId = ? AND t.destinationKeyTierId = ? AND t.destinationKeyTypeId = ?)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 1 AND t.destinationItem = 3 AND t.sourceKeyTierId = ? AND t.sourceKeyTypeId = ? AND t.destinationModId = ?)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 1 AND t.sourceModId = ?)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 2 AND t.sourceModId = ? AND t.destinationKeyTierId = ? AND t.destinationKeyTypeId = ?)
UNION ALL
(SELECT COUNT(1) AS count
FROM trades t
WHERE t.sourceItem = 2 AND t.destinationItem = 3 AND t.sourceModId = ? AND t.destinationModId = ?)
) AS derived
【问题讨论】:
-
正如你所说的
Now when obtaining a result set I want to count the amount of results to decide whether to display a message that no results were found, or not.你运行这个查询只是为了检查它是否会返回一些东西吗? -
@Manu 是的,处理查询更高级(有多个连接),但也使用
LIMIT和ORDER BY,平均包含 50 个结果 -
那么,当事先不知道要计数的请求组合时,您想知道有多少结果与
sourceItem和destinationItem的特定组合匹配?例如,第一个查询可能是“有多少 或 ?”,而下一个查询可能是“有多少 ”? -
@bishop 基本上是的,更新 2 之前的问题确实如此,但由于我刚刚添加了更新 3,因此还有更多因素需要注意。这……相当复杂
-
即使您进行了更新,组合点似乎仍然正确。如果是这样,您能否制作
sourceItem、destinationItem以及与您的计数位字段相关的任何其他字段?这样,每个可能的组合都会先验地被枚举出来,您可以使用简单的WHERE?