如何连接多个表,包括查找表并按行返回数据

Posted

技术标签:

【中文标题】如何连接多个表,包括查找表并按行返回数据【英文标题】:How to join multiple tables including lookup table and return data in rows 【发布时间】:2012-06-02 12:08:46 【问题描述】:

我正在尝试显示一些简单的计算机游戏结果,并让在我的代码中逐行遍历结果变得容易。我想要它,以便每场比赛的所有相关数据都在每条记录中,这样我就可以在一行上全部输出,例如:

A队(得分45)对B队(得分55),比赛时长:5 分钟 C队(得分60)对D队(得分65),比赛时长:4.3 分钟

因此,对于一场比赛,有两支球队互相比赛,每支球队都会在比赛结束时得分。基本上,games_teams 表中每场比赛最终都会有两行。

这是我的架构:

这是我的表格数据:

这是我想要实现的输出,因此我可以轻松地遍历结果并将它们输出到页面上:

我设法通过一些可怕的 SQL 和许多子查询来实现这一点:

SELECT games.game_id, game_name, game_duration, 
(SELECT team_id FROM games_teams WHERE games.game_id = games_teams.game_id LIMIT 0, 1) AS team_id_a,
(SELECT team_id FROM games_teams WHERE games.game_id = games_teams.game_id LIMIT 1, 1) AS team_id_b,
(SELECT teams.team_name FROM games_teams INNER JOIN teams ON games_teams.team_id = teams.team_id WHERE games.game_id = game_id LIMIT 0, 1) AS team_name_a,
(SELECT teams.team_name FROM games_teams INNER JOIN teams ON games_teams.team_id = teams.team_id WHERE games.game_id = game_id LIMIT 1, 1) AS team_name_b,
(SELECT team_score FROM games_teams WHERE games.game_id = games_teams.game_id LIMIT 0, 1) AS team_score_a,
(SELECT team_score FROM games_teams WHERE games.game_id = games_teams.game_id LIMIT 1, 1) AS team_score_b
FROM games

该方法的问题是它会很慢并且无法扩展。我还需要从 games_teams 表中提取其他游戏统计信息,这样子查询就会更多。

我尝试的另一种方法是:

我使用以下 SQL 实现了这一点:

SELECT games.game_id, game_name, game_duration, teams.team_id, team_name, team_score
FROM games
INNER JOIN games_teams ON games.game_id = games_teams.game_id
INNER JOIN teams ON games_teams.team_id = teams.team_id

现在这种方式将更难在代码中遍历,因为每个游戏的相关数据都在两个不同的记录中。我必须构建该行的第一部分,然后进入下一个循环迭代并打印下一部分。然后为下一场比赛重新开始,我试图在一行中显示所有信息,例如:

A队(45分)vs B队(55分),比赛时长:5分钟

所以这就是为什么我认为如果全部都在一个记录上会更容易。有没有办法很好地完成这一点,如果我需要在 games_teams 表中添加更多列,它也可以扩展?

Here's a pastebin link 如果需要重新创建数据库代码。

非常感谢任何帮助,谢谢!

【问题讨论】:

【参考方案1】:

您需要加入 games_teams 和团队两次,例如:

SELECT ga.game_id
        , ga.game_name
        , ga.game_duration
        , t1.team_name, gt1.team_score
        , t2.team_name, gt2.team_score
FROM games ga
JOIN games_teams  gt1 ON gt1.game_id = ga.game_id
JOIN games_teams  gt2 ON gt2.game_id = ga.game_id
JOIN teams t1 ON t1.team_id = gt1.team_id
JOIN teams t2 ON t2.team_id = gt2.team_id
WHERE gt1.team_id < gt2.team_id
        ;

挤出 games_teams * teams 子连接并引用它两次的干净方法是将其放入 CTE:(不幸的是 mysql 不支持 CTE)

WITH gtx AS (
        SELECT gt.game_id
        , gt.team_score
        , te.team_id
        , te.team_name
        FROM games_teams gt
        JOIN teams te ON te.team_id = gt.team_id
        )
SELECT ga.game_id 
        , ga.game_name
        , ga.game_duration
        , g1.team_name, g1.team_score
        , g2.team_name, g2.team_score
FROM games ga
JOIN gtx g1 ON g1.game_id = ga.game_id
JOIN gtx g2 ON g2.game_id = ga.game_id
WHERE g1.team_id < g2.team_id
  ;

结果:

 game_id | game_name | game_duration | team_name | team_score | team_name | team_score 
---------+-----------+---------------+-----------+------------+-----------+------------
       1 | Game A    |           300 | Team A    |         45 | Team B    |         55
       2 | Game B    |           258 | Team C    |         60 | Team D    |         65
(2 rows)

【讨论】:

顺便说一句:“对称破坏”条件(g1.team_id 你能举一个使用代理键game_team_id的例子吗? 不,我认为你应该自己做(因为这是显然家庭作业) 这不是家庭作业。我今年 28 岁。:\ WHERE gt1.game_team_id &lt; gt2.game_team_id 你是这个意思吗? 看起来不错。在实际订购 team1 和 team2 时可能会变得很重要,例如在家/外出踢足球,或下黑棋/下白棋。【参考方案2】:

这简化了,因为每场比赛总是正好有两支球队,因此您可以使用 UNION 来获取每场比赛的球队,如下所示:

SELECT q2.game_id, games.game_name, games.game_duration,
       q2.team_id_a, q2.team_id_b,
       teams_a.team_name AS team_name_a,
       teams_b.team_name AS team_name_b,
       games_teams_a.team_score AS team_score_a,
       games_teams_b.team_score AS team_score_b
FROM  (SELECT q.game_id,
              MAX(q.team_id_a) AS team_id_a,
              MAX(q.team_id_b) AS team_id_b
          FROM (
               SELECT games_teams.game_id,
                      MIN(games_teams.team_id) AS team_id_a,
                      0 AS team_id_b
               FROM   games_teams
               GROUP BY games_teams.game_id
            UNION
               SELECT games_teams.game_id,
                      0 AS team_id_a,
                      MAX(games_teams.team_id) AS team_id_b
               FROM   games_teams
               GROUP BY games_teams.game_id
         ) q
        GROUP BY q.game_id) q2
   INNER JOIN games ON q2.game_id = games.game_id
   INNER JOIN teams teams_a ON q2.team_id_a = teams_a.team_id
   INNER JOIN teams teams_b ON q2.team_id_b = teams_b.team_id
   INNER JOIN games_teams games_teams_a ON
              q2.game_id = games_teams_a.game_id AND q2.team_id_a = games_teams_a.team_id
   INNER JOIN games_teams games_teams_b ON
              q2.game_id = games_teams_b.game_id AND q2.team_id_b = games_teams_b.team_id;

【讨论】:

以上是关于如何连接多个表,包括查找表并按行返回数据的主要内容,如果未能解决你的问题,请参考以下文章

连接两个表并从一列返回多个匹配项的 SQL 查询?

比较两个 MYSQL 表并按列返回缺失的日期和分组

如何基于 ManyToManyField 内部连接表并按参数分组并在 Django 中获取最新的表?

EXCEL里如何查找列相同数值并按顺序返回行值

从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime

从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime