我一直在尝试构建一个大规模的查询,我已经成功并且能够真正完成查询。但是我从开发环境(小型数据库)到实时环境(大型数据库)的测试,我遇到了性能问题。
我认为答案可以在这里找到:https://dba.stackexchange.com/a/16376
但是真的没有别的办法吗?我甚至将子查询放在VIEW
中的原因是因为它们具有更复杂的结构。
VIEWS /查询示例:
pjl
查看:
(SELECT `pj`.`id` AS `id`,`pj`.`globalId` AS `globalId`,`pj`.`date` AS `date`,`pj`.`serverId` AS `serverId`,`pj`.`playerId` AS `playerId`,'playerjoins' AS `origin`
FROM `playerjoins` `pj`)
UNION ALL
(SELECT `pl`.`id` AS `id`,`pl`.`globalId` AS `globalId`,`pl`.`date` AS `date`,`pl`.`serverId` AS `serverId`,`pl`.`playerId` AS `playerId`,'playerleaves' AS `origin`
FROM `playerleaves` `pl`)
ll_below
查看:
SELECT `ll`.`id` AS `id`,`ll`.`globalId` AS `globalId`,`ll`.`date` AS `date`,`ll`.`serverId` AS `serverId`,`ll`.`gamemodeId` AS `gamemodeId`,`ll`.`mapId` AS `mapId`,`pjl`.`origin` AS `origin`,`pjl`.`date` AS `pjldate`,`pjl`.`playerId` AS `playerId`
FROM `pjl`
JOIN `levelsloaded` `ll`
ON `pjl`.`date` <= `ll`.`date`
现在简单的查询:
SELECT * FROM
(
(SELECT * FROM ll_below WHERE playerId = 976) llbelow
INNER JOIN
(SELECT id, MAX(pjldate) AS maxdate FROM ll_below WHERE playerId = 976 GROUP BY id) llbelow_inner
ON llbelow.id = llbelow_inner.id AND llbelow.pjldate = llbelow_inner.maxdate
)
WHERE origin = 'playerjoins'
ORDER BY date DESC
我可以将所有内容放在一个大问题中,但在我看来它会变得很乱。
我也知道为什么性能受到如此严重的打击,因为MySQL无法使用MERGE
算法来查看pjl
视图,因为其中有UNION ALL
。如果我将WHERE playerId = 976
子句放在正确的位置,那么性能命中率就会消失,但我也会有一个包含50行或其他内容的查询。
如果我想要表现并且查询仍然简洁,有人可以建议我该怎么做吗?
答案 0 :(得分:1)
本条:
WHERE origin ='playerjoins'
意味着您根本不需要执行UNION,因为在查询结束时您没有使用pl
中的任何行。
你认为视图可能强制使用临时表而不是使用合并算法。
UNION ALL还会创建自己的临时表。根据{{3}},这个案例在MySQL 5.7.3中进行了优化(截至本文撰写时仍为pre-alpha)。
此外,GROUP BY可能正在创建第三级临时表。
我发现您还在进行最大n组/ 操作,以便将行与每个ID的最大日期进行匹配。这种类型的操作有不同的解决方案,它们不使用子查询。例如,请参阅我的答案:
根据行数和其他条件,我已经看到两个针对每组最大查询的解决方案都能提供更好的性能。因此,您应该测试两种解决方案,并根据数据的状态和大小确定哪种解决方案更好。
我认为你应该解开视图,工会和子查询。在进行连接和聚合之前,查看是否可以直接对基表应用各种WHERE条件(如playerId=976
)。这应该会大大减少检查行的数量,并避免由视图和union以及group by引起的多层临时表。
重新评论:
您似乎想要的查询是针对特定玩家的每个级别的最新加入。
这样的事情:
SELECT ll.id,
ll.globalId,
ll.date AS leveldate,
ll.serverId,
ll.gamemodeId,
ll.mapId,
pj.date AS joindate,
pj.playerId
FROM levelsloaded AS ll
INNER JOIN playerjoins AS pj
ON pj.date <= ll.date
LEFT OUTER JOIN playerjoins AS pj2
ON pj.playerId = pj2.playerId AND pj2.date <= ll.date AND pj.date < pj2.date
WHERE pj.playerId = 976
AND pj2.playerID IS NULL
ORDER BY joindate DESC
(我没有测试过这个查询,但它应该让你开始。)
答案 1 :(得分:0)
比尔是绝对正确的......你的观点甚至没有真正提供任何好处。我试图为你建造一些东西,但我的解释可能并不完全正确。首先问自己简单的说法我想要得到什么。这就是我想出来的。
我正在寻找一个单独的玩家(因此你的玩家ID = 976)。我也只是考虑了PLAYERJOINS实例(不是那个击倒联盟部分的球员)。对于这个玩家,我想要他们加入游戏的最近日期。从该日期开始,我想要在加入的最大日期或之后创建的所有级别已加载。
所以,第一个查询只不过是playerJoined表中玩家976的最大日期。谁在乎任何其他用户或任何其他用户。 ID在这里与通过连接在LevelsLoaded表中的ID相同,因此获得该玩家ID和同一人的相同级别的加载ID是IMO,毫无意义。然后,从同一个人的最大日期之后/之后的级别中获取剩余的细节,按任何顺序排序..
如果我对您的查询的解释不正确,请提供明确的调整说明。
SELECT
ll.id,
ll.globalId,
ll.`date`,
ll.serverId,
ll.gamemodeId,
ll.mapId,
'playerjoins' as origin,
playerMax.MaxDate AS pjldate
FROM
( SELECT MAX( pj.`date` ) as MaxDate
FROM playerjoins pj
where pj.id = 976 ) playerMax
JOIN levelsloaded ll
ON ll.id = 976
AND playerMax.MaxDate <= ll.`date`