MySQL中锦标赛计分表的查询优化

Posted

技术标签:

【中文标题】MySQL中锦标赛计分表的查询优化【英文标题】:Query optimization for tournament scoring tables in MySQL 【发布时间】:2013-03-22 14:38:44 【问题描述】:

我在锦标赛相关数据库中有两个表,我需要知道最优化的 SQL 查询以生成正确的整体结果。结果必须显示总得分,减去任何罚分,并且应根据首先达到该分数的人打破并列的分数。

在数据库表中,我有一个事件日志,其中每个分数都是随着球队进行比赛而添加的,我还有另一个表格,显示哪个球队是哪个锦标赛的一部分。

Table "xTournamentTeam" (connects a team to a tournament)
=======================
+-----+------------+--------+--------------+
| nID | Team Name  | TeamID | TournamentID |
+-----+------------+--------+--------------+
|  1  | Team A     | 12     | 25           |
|  2  | Team B     | 13     | 25           |
|  3  | Team C     | 14     | 25           |
|  4  | Team D     | 15     | 25           |
|  3  | Team A     | 12     | 32           |
|  4  | Team B     | 13     | 32           |
+-----+------------+--------+--------------+

Table "nEventLog" (records scoring during a tournament)
=================
+-----+---------------+---------+----------+----------------+-----------------------+
| nID | nTournamentID | nTeamID | nPoints  | nPointsPenalty | nEventTime            |
+-----+---------------+---------+----------+----------------|-----------------------+
|  1  | 25            | 15      | 100      | 0              | 1/24/2013 6:05:14 AM  |
|  2  | 25            | 14      | 100      | 0              | 1/24/2013 6:29:55 AM  |
|  3  | 25            | 14      | 100      | 25             | 1/24/2013 7:09:34 AM  |
|  4  | 25            | 12      | 100      | 0              | 1/24/2013 7:12:28 AM  |
|  5  | 25            | 12      | 100      | 0              | 1/24/2013 8:42:59 AM  |
|  6  | 25            | 12      | 100      | 50             | 1/24/2013 8:43:36 AM  |
|  7  | 25            | 14      | 100      | 0              | 1/24/2013 9:15:24 AM  |
|  8  | 25            | 15      | 100      | 0              | 1/24/2013 9:15:27 AM  |
|  9  | 32            | 12      | 100      | 0              | 1/28/2013 8:33:49 AM  |
|  10 | 32            | 13      | 100      | 25             | 1/28/2013 2:15:12 PM  |
|  11 | 32            | 12      | 100      | 10             | 1/28/2013 7:12:25 AM  |
|  12 | 32            | 13      | 100      | 0              | 1/29/2013 7:18:06 AM  |
+-----+---------------+---------+----------+----------------+-----------------------+

在上述数据的情况下,我需要的查询应该为锦标赛 #25 生成以下结果:

+-------+------------+--------+--------------+---------------+---------------------+-----------------------------+
| nRank | Team Name  | TeamID | TournamentID | nTotalPoints  | nTotalPointsPenalty | nLatestEventTime            |
+-------+------------+--------+--------------+---------------+---------------------+-----------------------------+
|  1    | Team A     | 12     | 25           | 300           | 50                  | 1/24/2013 8:43:36 AM        |
|  2    | Team C     | 14     | 25           | 300           | 25                  | 1/24/2013 9:15:24 AM        |
|  3    | Team D     | 15     | 25           | 200           | 0                   | 1/24/2013 9:15:27 AM        |
|  4    | Team B     | 13     | 25           | 0             | 0                   |                             |
+-------+------------+--------+--------------+---------------+---------------------+-----------------------------+

出于加载目的,我试图不惜一切代价避免子查询,因为最终查询应尽可能优化。 “nRank”列可以通过编程方式生成...... mysql 不应该返回它,但我将它作为参考。

我最接近的查询是这个,但它没有返回“Team B”,因为他们在“nEventLog”表中没有任何记录 nTournamentID #25:

SELECT xTournamentTeam.nTeamName
     , sum(nEventLog.nPoints) AS nTotalPoints
     , xTournamentTeam.nTeamID
     , max(nEventLog.nEventTime) AS nLatestEventTime
     , sum(nEventLog.nPointsPenalty) AS nTotalPenaltyPoints
     , xTournamentTeam.nTournamentID
FROM
  xTournamentTeam
LEFT OUTER JOIN nEventLog
ON xTournamentTeam.nTeamID = nEventLog.nTeamID
WHERE
  xTournamentTeam.nTournamentID = 33
  AND nEventLog.nTournamentID = 33
GROUP BY
  xTournamentTeam.nID
, xTournamentTeam.nTournamentID
ORDER BY
  nTotalPoints DESC
, nLatestEventTime DESC

我当然不是 MySQL 查询方面的专家,而且我已经为此工作了两天,但没有取得多大成功,因此我们将不胜感激。

【问题讨论】:

您是否在您的 select 语句上运行了 explain “出于加载目的,我试图不惜一切代价避免子查询,因为最终查询应尽可能优化。”是什么意思?你能记录下为什么不优化子查询吗?这与我对 MySQL 和一般 SQL 优化器的了解背道而驰。 @GordonLinoff 我想我已经看到了几篇反对使用子查询朝那个方向发展的帖子。据我所知,对于可以通过子查询完成的事情,他们对服务器施加了太大的压力。我对他们没有任何反对意见,但我确实非常需要最优化的查询。目前,我正在使用多个查询和 php 执行上述操作,并且在锦标赛期间服务器上的负载(有数千人访问统计页面并不断刷新)非常糟糕,因此优化是关键。 @迈克尔。 . .几乎所有 SQL 引擎的执行引擎对子查询一无所知。他们正在执行数据流操作。子查询是语法约定。它们可能会也可能不会针对给定的引擎进行优化,但大多数 SQL 结构都是如此。 @Daedalus 因为我不是专家,所以 EXPLAIN 的结果并不能告诉我太多,但它们在这里: 【参考方案1】:

我稍微改变一下你的逻辑,我认为它起作用了:

SELECT
          xTournamentTeam.TeamName
         , sum(nEventLog.nPoints) AS nTotalPoints
         , xTournamentTeam.TeamID
         , max(nEventLog.nEventTime) AS nLatestEventTime
         , sum(nEventLog.nPointsPenalty) AS nTotalPenaltyPoints
         , xTournamentTeam.TournamentID
FROM
      xTournamentTeam 
LEFT OUTER JOIN nEventLog
    ON xTournamentTeam.TournamentID = nEventLog.nTournamentID AND xTournamentTeam.TeamID = nEventLog.nTeamID
WHERE
      xTournamentTeam.TournamentID = 25          
GROUP BY
      xTournamentTeam.TeamID
    , xTournamentTeam.TournamentID 
    , xTournamentTeam.TeamName
ORDER BY 
    nTotalPoints DESC

如果需要,可以将空值格式化为 0 或其他值。

【讨论】:

我现在正在对其进行一些测试,但到目前为止它看起来正在运行。 :) 只做了一处改动:“ORDER BY nTotalPoints DESC, nLatestEventTime”。在决胜局的情况下,这可以确保首先获得分数的球队获得领先。否则,您的 SQL 查询将完美运行!感谢您的帮助...您添加的“LEFT OUTER JOIN”语句很巧妙!

以上是关于MySQL中锦标赛计分表的查询优化的主要内容,如果未能解决你的问题,请参考以下文章

大表的 MySQL 查询优化

对第二个表中不存在 ID 的两个表的 MySQL 查询优化

mysql多条件查询的优化

MySQL查询优化

Mysql sql语句优化经验总结

MySQL查询优化器工作原理解析