MySQL构造一个查询

时间:2013-11-29 21:20:34

标签: mysql sql performance view where

我一直在尝试构建一个大规模的查询,我已经成功并且能够真正完成查询。但是我从开发环境(小型数据库)到实时环境(大型数据库)的测试,我遇到了性能问题。

我认为答案可以在这里找到: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行或其他内容的查询。

如果我想要表现并且查询仍然简洁,有人可以建议我该怎么做吗?

2 个答案:

答案 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`