我正在尝试优化SQL查询,以期提高执行速度。
我有以下两个表:
CREATE TABLE IF NOT EXISTS `data` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`entry` varchar(255) NOT NULL,
`numDB` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `entry` (`entry`),
UNIQUE KEY `entry_numDB` (`entry`,`numDB`),
UNIQUE KEY `entry_numDB_id` (`id`,`entry`,`numDB`),
KEY `numDB` (`numDB`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `details` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dataID` bigint(20) NOT NULL,
`dbID` int(11) NOT NULL,
<removed - unimportant>
PRIMARY KEY (`id`),
KEY `dataID` (`dataID`),
KEY `dbID` (`dbID`),
KEY `dataID_dbID` (`dataID`,`dbID`),
<removed - unimportant>
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
条目(例如,'abc')作为“id = 1; entry = abc,numDB = 2”存储在表数据中,并且具有(至少)两个条目详细信息“id = 1,dataID = 1, dbID = 4“和”id = 2,dataID = 1,dbID = 17“,但是相同的dataID和dbID可以多次出现,例如”id = 3,dataID = 1,dbID = 17“,”id = 4 ,dataID = 1,dbID = 17“。
表数据中的总条目数:45.245.438
表格详细信息中的条目总数:126.608.661
现在我想从表数据中获取前50个条目,这些条目没有条件dbID = 4,按data.numDB排序。生成的查询是:
SELECT DISTINCT(data.entry), data.numDB FROM blacklists.data data INNER JOIN blacklists.details details ON details.dbID NOT IN (4) AND data.id = details.dataID ORDER BY data.numDB DESC LIMIT 50
(至少)需要10分钟的处理时间(我在10分钟后停止)。
这是EXPLAIN输出的内容:
EXPLAIN SELECT DISTINCT(data.entry),data.numDB FROM blacklists.data data INNER JOIN blacklists.details details details.dbID NOT IN(4)AND data.id = details.dataID ORDER BY data.numDB DESC LIMIT 50
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE data index PRIMARY,entry_numDB_id entry_numDB 261 NULL 45166874 Using index; Using temporary; Using filesort
1 SIMPLE details ref dataID,dbID,dataID_dbID dataID 8 blacklists.data.id 1 Using where; Distinct
不使用DISTINCT(或GROUP BY)会导致条目重复多次。
有没有办法改进此查询?我已经阅读了很多帮助页面和其他问题及其答案,但无法找到这些表格的解决方案。
答案 0 :(得分:0)
首先,我将dbID NOT IN (4)
更改为dbID <> 4
。 MySQL可能会正确地优化它,但我想确定。
其次,我会考虑对数据进行反规范化,在data
中放置一个表示所需条件的字段,允许您执行单表查询(这将更快地工作)。该字段既可以在应用程序中维护,也可以使用触发器维护。
答案 1 :(得分:0)
加入细节有点让你感到困惑。你并没有真正使用dbID!= 4进行过滤,因此它仍然需要扫描大多数表'数据'。这是子查询可能帮助您的区域。而不是查询所有表'数据'以查找有效的最后50行+使用dbID == 4连接表'详细信息'的任何行,可能手动限制到最后几千行,或任何不具有的近似值一个dbID == 4
我认为通过这样编写查询可以获得很好的性能提升:
SELECT DISTINCT(data.entry),
data.numDB
FROM
(
SELECT x.entry, x.numDB, x.numDB
FROM blacklists.data x
ORDER BY x.numDB DESC LIMIT 2000
) data
INNER JOIN blacklists.details details
ON details.dbID NOT IN (4)
AND data.id = details.dataID
ORDER BY data.numDB DESC LIMIT 50
根据您的需要更改或更改子选择中的限制。较小的值将更快地运行此查询,但可能无法提供您想要的50条记录,较大的值将运行得较慢,但是您可以更好地获得所需的50条记录。
答案 2 :(得分:0)
我想知道您是否对以下查询有建议:
详细信息表保持与上面相同,我还有
CREATE TABLE IF NOT EXISTS `ips` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dataID` bigint(20) NOT NULL,
`ip` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `dataID_ip` (`dataID`,`ip`),
KEY `dataID` (`dataID`),
KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
我现在想从该表获取所有不在dbID = 4中的IP,按其计数(ip)排序:
我的工作是:
SELECT ip.id, ip.ip, count(ip.ip)
FROM
(
SELECT x.id, x.ip, x.dataID
FROM ips x
GROUP BY x.ip ORDER BY COUNT(x.ip) DESC LIMIT 1000
) ip
INNER JOIN details details
ON details.dbID NOT IN (4)
AND ip.dataID = details.dataID
GROUP BY ip.ip ORDER BY COUNT(ip.ip) DESC LIMIT 50
然而内部SELECT需要全表扫描。
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 1000 Using temporary; Using filesort
1 PRIMARY details ref dataID,dbID,dataID_dbID,dataID_active,dbID_active_... dataID 8 ip.dataID 1 Using where
2 DERIVED x index NULL ip 4 NULL 8960260 Using temporary; Using filesort
有没有办法改进这个查询?