【问题标题】:MySQL JOIN with SUM and 3 tablesMySQL JOIN 与 SUM 和 3 个表
【发布时间】:2012-06-17 22:47:23
【问题描述】:

我有以下表格(球员、赛事、分数)

我想要一份每个球员在 2012 年获得多少积分的列表。 我希望列表中包含所有球员,即使有些球员在 2012 年还没有参加比赛。

所以理论上它应该打印:

Ola Hansen     6  
Tove Svendson  0  
Kari Pettersen 0

我试过了:

SELECT *, sum(Points) as Points FROM Players
LEFT OUTER JOIN Scores ON P_Id=Player
LEFT OUTER JOIN Events ON Event=E_Id AND Year='2012' 
GROUP by P_Id ORDER by Points DESC

但这算了所有年份的所有分数。

【问题讨论】:

  • 我猜这是 MySQL,因为没有其他 RDBMS 会允许这种可怕的 GROUP BY 和聚合?

标签: mysql join sum


【解决方案1】:

由于人们(包括我自己)喜欢将“最重要”或“主题”表放在首位,因此您可以通过首先发生事件和分数之间的连接并作为 INNER JOIN 来完成您要查找的内容:

SELECT
   P.P_Id,
   P.LastName,
   P.FirstName,
   IsNull(Sum(P.Points), 0) TotalPoints
FROM
   Players P
   LEFT JOIN (
      Events E
      INNER JOIN Scores S
         ON E.Event = S.E_Id
         AND E.Year = '2012'
   ) ON P.P_Id = S.Player
GROUP BY P.P_Id, P.LastName, P.FirstName -- no silly MYSQL implicit grouping for me!
ORDER BY TotalPoints DESC

强制连接顺序的诀窍在于,实际上是 ON 子句的位置执行此操作 - 您可以删除括号,它应该仍然有效。但我更喜欢使用它们,因为我认为额外的清晰度是必须的。

通过将 INNER JOIN 放在最前面,将 OUTER 放在最后,这也是强制执行连接顺序的一种完全有效的方法。只是,您希望最后一张桌子决定最后一组的成员资格。因此,将其切换为 RIGHT JOIN 即可:

SELECT
   P.P_Id,
   P.LastName,
   P.FirstName,
   IsNull(Sum(P.Points), 0) TotalPoints
FROM
   Events E
   INNER JOIN Scores S
      ON E.Event = S.E_Id
      AND E.Year = '2012'
   RIGHT JOIN Players P
      ON S.Player = P.P_Id
GROUP BY P.P_Id, P.LastName, P.FirstName
ORDER BY TotalPoints DESC

我觉得有必要补充一点,在我看来,在不同表中使用不同名称作为 PK 与 FK 的列是对最佳实践的严重违反。应该是所有表中的“P_Id”,或者所有表中的“Player”,不能不一致。

【讨论】:

  • 他不是在谈论一组主键表中的愚蠢,他是说他不喜欢引用表中的列名与主键表中的列名不同,在这种情况下,如果玩家.P_Id 是 Pk,在 Score 中应该是 Scores.P_Id 而不是 Scores.Player,我同意 erike。维护标准
  • 哦,哈哈,你说的是group by,在MysQl中你可以通过一个“*”来进行group by,这真的很傻,应该是列
  • @Mr.我可以接受GROUP BY * 甚至GROUP BY Column, REMAINDER,因为这样会使其明确,但隐式分组很可怕。
【解决方案2】:
SELECT P.FIRSTNAME, P.LASTNAME,  A.Points as Points 
FROM Players P
LEFT OUTER JOIN 
(
   SELECT S.Player, sum(Points) 
   FROM Scores S, Events E 
   WHERE S.Event=E.E_Id 
   AND E.Year='2012' 
   GROUP BY S.Player
) A 
ON A.PLAYER = P.P_ID
ORDER by Points DESC

基本上,您需要在事件和分数之间使用INNER JOIN 来强制将 2012 年的分数和 LEFT OUTER JOIN 与此子结果相加,因为您希望所有玩家 + 他们在 2012 年的分数。

【讨论】:

    【解决方案3】:

    试试这个:

    select firstName, lastName, sum(if(year is null, 0, points)) points from p
    left join s on p_id = player
    left join e on e_id = event and year = 2012
    group by p_id, lastName, firstName
    order by points desc
    

    我想它应该比子查询执行得更好......但便携性会降低。试试看!

    这里是fiddle

    【讨论】:

      【解决方案4】:

      分数和事件需要在内连接将它们外连接到玩家。

      我们可以使用子查询或括号来强制这个特定的连接“优先级”,但最好只使用 SQL 文本中的 JOIN 顺序,然后小心地将最后一个 JOIN“定向”到玩家(在这种情况下是正确的) )。

      COALESCE 仅用于将 NULL 转换为 0。

      SELECT
          P_Id, LastName, FirstName, COALESCE(SUM(Points), 0) TotalPoints
      FROM
          Scores
          JOIN Events
              ON Event = E_Id AND Year = 2012
          RIGHT JOIN Players
              ON P_Id = Player
      GROUP BY
          P_Id, LastName, FirstName
      ORDER BY
          TotalPoints DESC;
      

      这会产生:

      P_ID    LASTNAME    FIRSTNAME   TOTALPOINTS
      1       Hansen      Ola         6
      2       Svendson    Tove        0
      3       Pettersen   Kari        0
      

      你可以在这个SQL Fiddle玩它。

      【讨论】:

        猜你喜欢
        • 2014-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-11
        • 2022-11-11
        • 2013-04-19
        • 1970-01-01
        • 2014-09-06
        相关资源
        最近更新 更多