简体   繁体   English

MySQL加入SUM和3个表

[英]MySQL JOIN with SUM and 3 tables

I have the following tables (Players, Events, Scores) 我有以下表格(玩家,事件,分数)

在此输入图像描述

I would like a list of how many points each player has got in 2012. I want the list to feature all of the players, even though some haven't played in 2012. 我想列出每位玩家在2012年获得多少积分。我希望列表能够包含所有玩家,尽管有些玩家在2012年还没玩过。

So in theory it should print: 所以在理论上它应该打印:

Ola Hansen     6  
Tove Svendson  0  
Kari Pettersen 0

I tried: 我试过了:

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

But that counts all the points from all the years. 但这包括了所有年份的所有要点。

Scores and events need to be inner-joined before outer-joining them to players. 分数和事件需要外部加入玩家之前进行内部联接。

We could use a subquery or parentheses to force this particular join "precedence", but it's nicer to just use the order of JOINs in the SQL text, and then carefully "orient" the last JOIN to players (RIGHT in this case). 我们可以使用子查询或括号来强制这个特定连接“优先”,但是在SQL文本中使用JOIN的顺序更好,然后小心地将最后一个JOIN“定向”给玩家(在这种情况下为RIGHT)。

The COALESCE is just for converting NULLs to 0s. 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;

This produces: 这会产生:

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

You can play with it in this SQL Fiddle . 你可以在这个SQL小提琴中玩它。

Since people (myself included) like to put the "most important" or "subject" table first, you can accomplish what you're looking for by making the join between Events and Scores occur first and as an 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

The trick about enforcing join order is that really it's the location of the ON clause that does it—you can remove the parentheses and it should still work. 关于强制连接顺序的诀窍在于它实际上是ON子句的位置 - 你可以删除括号并且它仍然可以工作。 But I prefer to use them because I think the extra clarity is a must. 但我更喜欢使用它们,因为我认为额外的清晰度是必须的。

It's also a perfectly valid way to enforce join order by putting your INNER JOINs first and the OUTER last. 它也是一种非常有效的方法来强制执行连接顺序,方法是先将INNER JOIN和OUTER放在最后。 Only, you want the last table to be the one that dictates membership in the final set. 只是,您希望最后一个表成为决定最终集合成员资格的表。 So switching it to RIGHT JOIN will do the trick: 所以将它切换到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

I feel compelled to add that using different names for a column when it is PK vs. FK in different tables is in my mind a really bad violation of best practice. 我觉得有必要补充一点,当不同表中的PK与FK相比时,对于列使用不同的名称在我看来是违反最佳实践的。 It should be "P_Id" in all tables, or "Player" in all tables, and not be inconsistent. 它应该是所有表中的“P_Id”,或者所有表中的“Player”,并且不应该是不一致的。

Try this: 试试这个:

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

I guess it should perform better than a subquery... but will be less portable. 我猜它应该比子查询表现得更好......但是便携性会降低。 Give it a try! 试试看!

Here is the fiddle . 这是小提琴

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

basically you need an INNER JOIN between events and Scores to force the sum of the points of the 2012 and a LEFT OUTER JOIN with this subresult, because you want all the players + their points in the 2012. 基本上你需要在事件和分数之间使用INNER JOIN来强制2012年的点数和这个子结果的LEFT OUTER JOIN总和,因为你想要所有的球员+他们在2012年的积分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM