Reputation: 174
I have a MySQL query that I'd like help optimizing. Its rather simple in nature but its taking about 0.5 seconds to run and this is going to be run very often.
SET @RankNumber:=0;
SELECT NumberRank,TankName, Color
FROM
(
SELECT @RankNumber := @RankNumber + 1 AS NumberRank, TankName, Color
FROM
(
SELECT TankID, MapID, Color, TankName
FROM MAPDATA
INNER JOIN Tank ON Tank.ID = MapData.TankID
WHERE MapID = 3
ORDER BY Rank DESC, TotalPP DESC
) Query1
) final
WHERE Color = 3
LIMIT 10;
Essentially what I'm doing, is im giving each record a number (RankNumber) and then I'm selecting all of the tanks from the database where the map is 3, then I give each of them the number in the map orders by rank and experience points. THEN I filter out all except the color I want and this is why I have the three levels essentially so I can get each line.
You have top 10, which is simple 1-10 then you have top purple, which may look more like 1,7,10,25,36 etc etc since the top 10 aren't the top 10 of all colors. Make sense? This is taking .5 seconds to complete and its hit by people probably 10-30 times a minute.
1 PRIMARY <derived2> ALL 31220 Using where
2 DERIVED <derived3> ALL 31220
3 DERIVED Tank ALL PRIMARY,idx_TankID 9863 Using temporary; Using filesort
3 DERIVED MAPDATA ref PRIMARY,MapID,TankID,idx_mapdata PRIMARY 8 microtanksbeta.Tank.ID 1
Here is my EXPLAIN on that query. Any help would be appreciated as to speeding this up. If there is a way i can tell it to keep this query in memory or something that works too just something so its not so slow.
SHOW CREATE TABLES
'MapData', 'CREATE TABLE `mapdata` (
`MapID` int(11) NOT NULL,
`TankID` int(11) NOT NULL,
`Color` tinyint(4) NOT NULL,
`X` int(11) DEFAULT ''-1'',
`Y` int(11) DEFAULT ''-1'',
`Rank` tinyint(4) NOT NULL DEFAULT ''0'',
`Health` int(11) NOT NULL DEFAULT ''1000'',
`Armors` tinyint(4) NOT NULL DEFAULT ''0'',
`Duals` tinyint(4) NOT NULL DEFAULT ''0'',
`Missiles` tinyint(4) NOT NULL DEFAULT ''0'',
`Homings` tinyint(4) NOT NULL DEFAULT ''0'',
`Radars` tinyint(4) NOT NULL DEFAULT ''0'',
`Beacons` tinyint(4) NOT NULL DEFAULT ''0'',
`HasRankKill` bit(1) NOT NULL DEFAULT b''0'',
`TotalPP` bigint(20) NOT NULL DEFAULT ''0'',
`RankPP` bigint(20) NOT NULL DEFAULT ''0'',
`KillCount` int(11) NOT NULL DEFAULT ''0'',
`DeathCount` int(11) NOT NULL DEFAULT ''0'',
`TimePlayed` time NOT NULL DEFAULT ''00:00:00'',
`EnabledEquipment` tinyint(4) NOT NULL DEFAULT ''0'',
`Prestige` tinyint(4) NOT NULL DEFAULT ''0'',
PRIMARY KEY (`MapID`,`TankID`,`Color`),
KEY `MapID` (`MapID`),
KEY `TankID` (`TankID`),
KEY `idx_mapdata` (`MapID`,`Color`,`TankID`),
CONSTRAINT `mapdata_ibfk_1` FOREIGN KEY (`MapID`) REFERENCES `maps` (`ID`) ON DELETE CASCADE,
CONSTRAINT `mapdata_ibfk_2` FOREIGN KEY (`TankID`) REFERENCES `tank` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1'
'Tank', 'CREATE TABLE `tank` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Username` text NOT NULL,
`TankName` text NOT NULL,
`KillAward` int(11) DEFAULT ''0'',
`DeathAward` int(11) DEFAULT ''0'',
`TimeAward` int(11) DEFAULT ''0'',
`Stars` int(11) DEFAULT ''0'',
`Trophy` int(11) DEFAULT ''0'',
`Dot` int(11) DEFAULT ''0'',
`Cartographer` int(11) DEFAULT ''0'',
`OtherAwards` int(11) DEFAULT ''0'',
`Banned` bit(1) DEFAULT b''0'',
`BanExpiration` datetime DEFAULT NULL,
`BanExplination` text,
`Deleted` bit(1) DEFAULT b''0'',
`DeletedDate` datetime DEFAULT NULL,
`CreationDate` datetime NOT NULL DEFAULT ''2011-05-24 00:00:00'',
PRIMARY KEY (`ID`),
KEY `idx_TankName` (`TankName`(40)),
KEY `idx_UNTankName` (`Username`(60),`TankName`(40)),
KEY `idx_TankID` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=23239 DEFAULT CHARSET=latin1'
Upvotes: 1
Views: 104
Reputation: 39961
I don't see any use for 3 levels, 2 should be enough. Also I've moved the join out of the inner select to make that one faster.
SET @RankNumber:=0;
SELECT inner.NumberRank, tank.TankName, inner.Color
FROM (SELECT @RankNumber := @RankNumber + 1 AS NumberRank, TankID, Color
FROM MAPDATA
WHERE MapID = 3
ORDER BY Rank DESC, TotalPP DESC
) inner
INNER JOIN Tank ON Tank.ID = inner.TankID
WHERE inner.Color = 3
LIMIT 10;
Make sure that the inner query alone uses a index and is fast. Try that and see if you get the same result and if it's faster.
Upvotes: 1
Reputation: 32094
SELECT NumberRank, TankName, Color
FROM (
SELECT @RankNumber := @RankNumber + 1 AS NumberRank, TankID, Color
FROM MAPDATA, (SELECT @RankNumber:=0) as init
WHERE MapID = 3
HAVING Color = 3
ORDER BY Rank DESC, TotalPP DESC
LIMIT 10
) ranks JOIN Tank ON Tank.ID = TankID
Upvotes: 0