【问题标题】:Help with a SQL query joining multiple tables帮助连接多个表的 SQL 查询
【发布时间】:2011-08-12 14:03:26
【问题描述】:

首先我将解释这个案例,我有一个表tbl_game,它的结构就是这样。此表包含游戏开始时间和配对玩游戏的时间

|    id    |     time     |     pair_id    |
-----------+--------------+ ---------------
1          | 123123123    |      1         |
2          | 123168877    |      1         |

我还有另一张桌子tbl_throws,它保存着每个玩家的得分。如果您想知道,this a basic dice rolling game

|   id     |    game_id   |   player_id   |  score  |
-----------+--------------+---------------+---------|
|    1     |    1         |   1           |   2     | 
|    2     |    1         |   2           |   5     |
|    3     |    1         |   1           |   9     |
|    4     |    1         |   2           |   11    |
|    5     |    2         |   1           |   7     | 
|    6     |    2         |   2           |   6     |

现在,id 这里是投掷 id,而不是游戏 id。在这里,每个拥有player_id 1 和 2 的玩家都掷了两次骰子,并在同一场比赛中得到了各自的分数,而在另一场比赛中只有一次

现在,使用这两个表,我需要创建一个记录集,即每个玩家在一场比赛中的总得分

|  game_id   | game_time | player1_total | player2_total|
|------------+-----------+---------------+--------------|
|     1      | 123123123 |     11        |     16       |
|     2      | 123168877 |     7         |     6        |

我尝试了很多莫名其妙的巨型查询,但没有给出正确的结果? 对此的正确查询是什么?

更新

因为,大多数答案都受到一个事实的限制,即 player1id 和 player2id 必须是已知的或固定的。

因此,我即将提供的信息可能有助于消除混淆。 还有另一个表,其中保存了玩家的信息。 tbl_pupil 结构如下

|   id    |   unique_id   | name     |
|---------+---------------+----------|
|   1     |     001       | some     |
|   2     |     002       | another  |

而这些玩家统称为另一桌的一对tbl_pair

|   id    |   player1     | player2  |
|---------+---------------+----------|
|   1     |     1         | 2        |

So, now 
select 
    g.id
    g.time
    p1.id as player1id
    p1.name as player1name
    t.score as player1score
    p2.id as player2id
    p2.name as player2name
    t.score as player2score
FROM 
    tbl_game g,
    inner join tbl_pair as pair on g.pair_id = pair.id
    inner join tbl_pupil as p1 on p1.id = pair.player1
    inner join tbl_pupil as p2 on p2.id = pair.player2
    inner join tbl_throw as t on g.id = t.game_id

这是我的初步查询,它以这样的方式带来了记录集

|   id    |   time  |   player1id    |  player1name |   player1score  |   player2id   |    player2name   |  player2score   |
----------------------------------------------------------------------------------------------------------------------------
|   1     |   12    |   1            |  some        |   5             |   2           |    another       |  2              |
|   1     |   12    |   1            |  some        |   5             |   2           |    another       |  5              |
|   1     |   12    |   1            |  some        |   9             |   2           |    another       |  9              |
|   1     |   12    |   1            |  some        |   11            |   2           |    another       |  11             |

现在我只是顺便显示一个游戏 id 的结果。我没有保存足够的知识,将上述记录归为一个,其中 player1 单独的总分在一列中,而 playe2 的单独总分总和在另一列中。

【问题讨论】:

  • 您使用的是什么类型的数据库? MSSQL、MySQL?
  • @Jon - 他在评论中添加了 SQL Server,我添加了标签。
  • 解决问题的一种方法:首先,获取每个玩家在每场比赛中的总分。然后,将该结果集加入自身,以获得同一行中两个玩家的分数。我发布的答案有一个这种方法的例子。

标签: sql sql-server pivot-table


【解决方案1】:

试试这个:

SELECT 
  tbl_game.id AS game_id, 
  tbl_game.time AS game_time, 
  SUM(CASE WHEN player_id = 1 THEN score ELSE 0 END) AS player1_total, 
  SUM(CASE WHEN player_id = 2 THEN score ELSE 0 END) AS player2_total
FROM tbl_game JOIN tbl_thorws ON tbl_game.id = tbl_throws.game_id
GROUP BY tbl_game.id

【讨论】:

  • 请记住,这只是MySQL 中的有效语法,因为您在Game_time 中有一个非聚合非分组字段
  • 但是player_id怎么是固定的,不会只有2个玩家,我只是设置一个例子
  • 我正在做的是用于 vb.net 应用程序的 SQL Server,所以,php 是不可选择的。 @JNK,这是用于生成报告,因此添加多个玩家不是一个可行的选择
  • @mrn - 好吧,迈克尔和我向您展示了如何进行您所追求的查询。如果您在编写应用程序方面需要帮助,请打开另一个问题。
  • @Jon - 当您将 Player1Player2 硬编码为字段名称时,我不确定您怎么能这么说。此外,他想要超过 2 是他所说的,而不是数字会有所不同。
【解决方案2】:

这与其他一些答案类似,但关键不取决于玩家 ID 为 1 和 2。

select
    game_id = g.id,
    game_time = g.time,
    player1_total = SUM(case t.player_id when p.player1_id then t.score else 0 end),
    player2_total = SUM(case t.player_id when p.player2_id then t.score else 0 end)
from
    tbl_game g
    join tbl_throws t on g.id = t.game_id
    join ( --Get the player IDs for this game
        select 
            game_id,
            player1_id = MIN(player_id),
            player2_id = MAX(player_id)
        from
            tbl_throws 
        group by game_id
    ) p     
        on p.game_id = t.game_id
group by 
    g.id, g.time

只是为了好玩,我将以上内容概括为允许更多 > 2 名玩家:

这 2 个 CTE 表只显示了我正在使用的测试数据

;WITH tbl_game as (
    select ID = 1, time = 123123123, pair_id = 1
    union select ID = 2, time = 123168877, pair_id = 1
),
tbl_throws as (

select id = 1, game_id = 1, player_id = 1, score = 2
union select id = 2, game_id = 1, player_id = 2, score = 5
union select id = 2, game_id = 1, player_id = 3, score = 5
union select id = 3, game_id = 1, player_id = 1, score = 9
union select id = 4, game_id = 1, player_id = 2, score = 11
union select id = 5, game_id = 2, player_id = 1, score = 7
union select id = 6, game_id = 2, player_id = 2, score = 6
)
select
    game_id = g.id,
    game_time = g.time,
    player1_id = MAX(case x.player_no when 1 then t.player_id else 0 end),
    player1_total = SUM(case x.player_no when 1 then t.score else 0 end),
    player1_id = MAX(case x.player_no when 2 then t.player_id else 0 end),
    player2_total = SUM(case x.player_no when 2 then t.score else 0 end),
    player3_id = MAX(case x.player_no when 3 then t.player_id else 0 end),
    player3_total = SUM(case x.player_no when 3 then t.score else 0 end),
    player4_id = MAX(case x.player_no when 4 then t.player_id else 0 end),
    player4_total = SUM(case x.player_no when 4 then t.score else 0 end)
    /* Add more rows for the number of players permitted in a single game */
from
    tbl_game g
    join tbl_throws t on g.id = t.game_id
    cross apply (
        select player_no = COUNT(distinct player_id) 
        from tbl_throws sub 
        where sub.player_id <= t.player_id 
                and Sub.game_id = t.game_id
    ) x
group by 
    g.id, g.time

【讨论】:

  • 为什么是 min(player_id) 和 max(player_id)
  • 它允许无论两个玩家 ID 是什么 - 不强制他们为 1 和 2 - 它基本上是说,将玩家 1 视为较低的玩家 ID,将玩家 2 视为较高的玩家 ID。
  • 但是如果玩家 1 是 2 而玩家 2 是 1 会怎样
  • 你必须给列命名!结果将显示在 player1_total 等中的最低 playerid 的结果。
  • 我已经更新了我的第二个查询,将每个分数对应的玩家 ID 输出为单独的列,这样就可以将它们全部重新组合在一起。
【解决方案3】:

您需要对这两个表进行内部连接,并汇总您的分数。在我使用CASE 语句按玩家聚合之后,您要做的基本支点。

SELECT  G.Id as Game_Id, 
        G.time as Game_Time,
        SUM(CASE WHEN t.Player_id = 1 THEN t.score ELSE 0 END) as Player1_total,
        SUM(CASE WHEN t.Player_id = 2 THEN t.score ELSE 0 END) as Player2_total
FROM tbl_game G
INNER JOIN tbl_throws T
    ON g.id = t.game_id
GROUP BY g.ID, g.time

【讨论】:

  • @jon - 复制粘贴问题,很快就解决了
  • 这适用于 player_id 值具体为 1 和 2,但不是更通用的 player_id 值。
  • @spencer - 是的,这就是问题在原始帖子中的呈现方式。
  • @JNK - 假设 tbl_player 将只包含两行,id 值为 1 和 2 是否合理?
  • @spencer - 因为 OP 从来没有表示 tbl_player 甚至 EXISTS 我会说假设它是不合理的。从最初的帖子看来,player_id 只是游戏中的一个序数,而不是对一个独特个体的引用。
【解决方案4】:

我认为这应该做你想做的事情。

SELECT 
  tbl_game.id as game_id, 
  tbl_game.time as game_time, 
  SUM(player1.score) as player1_total, 
  SUM(player2.score) as player2_total
FROM tbl_game
INNER JOIN tbl_throws player1 ON player1.game_id = tbl_game.id AND player1.player_id = 1
INNER JOIN tbl_throws player2 ON player2.game_id = tbl_game.id AND player2.player_id = 2
GROUP BY tbl_game.id, tbl_game.time

【讨论】:

  • 与迈克尔的回答相同的问题,仅在 MySQL 中有效
  • 是的,@Colin,您应该补充一点,在其他 DBMS 中,GROUP BY 应该是:GROUP BY tbl_game.id, tbl_game.time
  • 一个更严重的问题是这个查询会产生错误的结果(mini Cartesian Product issue)
  • 好点,我希望我有时间创建表格并测试一下!
【解决方案5】:
SELECT t.game_id
     , t.game_time
     , ( SELECT SUM(t.score)
         FROM tbl_throws AS t
         WHERE t.game_id = g.id
           AND player_id = 1
       ) AS player1_total
     , ( SELECT SUM(t.score)
         FROM tbl_throws AS t
         WHERE t.game_id = g.id
           AND player_id = 2
       ) AS player2_total
FROM tbl_game AS g 

【讨论】: