无法通过加入获得正确的计数

时间:2014-12-07 04:55:03

标签: mysql

我尝试使用下表计算球队中每位球员的进球数和助攻数。一名球员可以在一个赛季中参加多支球队比赛。我的结果计算了所有球队的总助攻数。我怎样才能将助攻限制在一支球队?

我的查询包含在下面。我期待的结果是Gretzky的2个进球和1个助攻。我不希望结果中包含辅助ID 3.

mysql> select * from players;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
|  1 | Wayne      | Gretzky   |
|  2 | Mario      | Lemieux   |
|  3 | Mark       | Messier   |
+----+------------+-----------+

mysql> select * from teams;
+----+-----------+
| id | team_name |
+----+-----------+
|  1 | Oilers    |
|  2 | Penguins  |
|  3 | Kings     |
+----+-----------+

mysql> select * from goals;
+----+-----------+---------+---------+
| id | player_id | team_id | game_id |
+----+-----------+---------+---------+
|  1 |         1 |       1 |       1 |
|  2 |         1 |       1 |       1 |
|  3 |         3 |       1 |       1 |
|  4 |         2 |       2 |       1 |
|  5 |         3 |       3 |       2 |
+----+-----------+---------+---------+

mysql> select * from assists;
+----+---------+-----------+
| id | goal_id | player_id |
+----+---------+-----------+
|  1 |       1 |         3 |
|  2 |       3 |         1 |
|  3 |       5 |         1 |
+----+---------+-----------+

mysql> select players.id, players.last_name,
    -> count(distinct goals.id) as 'Goals',
    -> count(distinct assists.id) as 'Assists'
    -> from players
    -> join goals on players.id = goals.player_id
    -> left join assists on players.id = assists.player_id
    -> join teams on goals.team_id = teams.id
    -> where teams.id = 1
    -> group by players.id;
+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  1 | Gretzky   |     2 |       2 |
|  3 | Messier   |     1 |       1 |
+----+-----------+-------+---------+

1 个答案:

答案 0 :(得分:1)

我认为,您尝试做的最直接的方法是使用相关的子查询。

所以,下面的第一个示例会返回您正在寻找的结果。您可以轻松修改它以排除零目标和助攻的行。

它在每个子查询中使用team_id值,但您可以使用变量或参数提供它,如图所示,这样您只需要指定一次值:

set @team_id := 2;

select
    p.id as player_id
    , p.last_name
    , (
        select count(*)
        from goals
        where player_id = p.id
        and team_id = @team_id
    ) as goals
    , (
        select count(*)
        from assists
        inner join goals on assists.goal_id = goals.id
        where assists.player_id = p.id
        and goals.team_id = @team_id
    ) as assists
from players p

对于第1队:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  1 | Gretzky   |     2 |       1 |
|  2 | Lemieux   |     0 |       0 |
|  3 | Messier   |     1 |       1 |
+----+-----------+-------+---------+

对于第2队:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  1 | Gretzky   |     0 |       0 |
|  2 | Lemieux   |     1 |       0 |
|  3 | Messier   |     0 |       0 |
+----+-----------+-------+---------+

对于第3队:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  1 | Gretzky   |     0 |       1 |
|  2 | Lemieux   |     0 |       0 |
|  3 | Messier   |     1 |       0 |
+----+-----------+-------+---------+

<强>后记

从使用较少的子查询和/或使用聚合查询尝试执行此操作的角度来看,第一次尝试时会遇到一些问题。

一个问题是,如果你不包含group by子句中的所有字段,你的查询可能无法正常工作,即使MySQL不会抱怨你(大多数?)其他数据库会。

另外,因为你的助攻和球员表中的记录只是通过目标表与球队间接相关,所以很难通过一个查询获得目标和助攻的独立汇总。

作为一种类型的例证,其他早期的答案,包括我的第一次快速拍摄,有几个问题:

  • 如果玩家有团队助攻,但该团队没有任何目标,则查询无法返回该玩家和团队组合的任何结果。结果不完整。

  • 如果一名球员有一支球队的进球,但该球队没有任何助攻,那么当他们应该返回零时,查询仍然会返回一个正数助攻。 结果实际上是错误的,而不仅仅是不完整的

下面是一个稍微更正确,但仍然不完整的解决方案。它正确地指示一个玩家是否没有助攻,虽然返回null而不是0,这是不幸的。

但这仍然是一个部分解决方案,因为如果一名球员没有球队的任何进球,你仍然不会看到该球员和球队组合的任何助攻。

这使用子查询作为虚拟表来聚合每个玩家和团队的助攻,而左外部联接到子查询是因为如果有目标但没有助攻则返回结果。

select
    p.id as player_id
    , p.last_name
    , count(g.game_id) as goals
    , a.assists
from players p
inner join goals g on p.id = g.player_id
left join (
    select
        assists.player_id
        , goals.team_id
        , count(assists.id) as assists
    from assists
    inner join goals on assists.goal_id = goals.id
    group by player_id, team_id, assists.id
) a
on g.player_id = a.player_id and g.team_id = a.team_id
where g.team_id = 1
group by player_id, last_name, g.team_id

该查询返回以下结果:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  1 | Gretzky   |     2 |       1 |
|  3 | Messier   |     1 |       1 |
+----+-----------+-------+---------+

为第2队运行这个,你得到了下一个结果,表明Lemieux没有为2队提供任何助攻,但是对于其他两名没有助攻且没有球队进球的球员都没有任何结果2:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  2 | Lemieux   |     1 |    null |
+----+-----------+-------+---------+

最后,为第3队运行它,你得到了下一个结果,表明Messier没有任何球队3的任何助攻。但Gretzky失踪了,尽管他确实为3队提供了助攻,因为他没有团队3没有任何目标。所以解决方案不完整:

+----+-----------+-------+---------+
| id | last_name | Goals | Assists |
+----+-----------+-------+---------+
|  3 | Messier   |     1 |    null |
+----+-----------+-------+---------+