MySQL JOIN 与 SUM 和 3 个表
Posted
技术标签:
【中文标题】MySQL JOIN 与 SUM 和 3 个表【英文标题】:MySQL JOIN with SUM and 3 tables 【发布时间】: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
和聚合?
【参考方案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玩它。
【讨论】:
以上是关于MySQL JOIN 与 SUM 和 3 个表的主要内容,如果未能解决你的问题,请参考以下文章