【问题标题】:An alternative to a (huge) nested loop (inner join)(巨大的)嵌套循环(内连接)的替代方案
【发布时间】:2014-08-18 23:38:07
【问题描述】:

请看一下这个 SELECT 语句:

SELECT
    b.player_id,
    COUNT(CASE WHEN a.team = m.team_win THEN 1 END),
    COUNT(CASE WHEN a.team <> m.team_win THEN 1 END)
FROM
    players a,
    players b,
JOIN
    matches m
    ON m.match_id = b.match_id
WHERE
    a.player_id <> b.player_id
    and a.team <> b.team
    and a.player_id = 100
GROUP BY
    b.player_id

完成后,该语句应显示一个记录集,其列是:

  • a.player_id 对阵的 b.player_id
  • count 的比赛中a.player_id 击败了b.player_id
  • a.player_idb.player_id 击败的比赛中的count

不幸的是,这些表相当大。 matches 大约有 160 万行。 players 大约有 1700 万行,因此加入它们会带来一些挑战:

执行计划索引查找playersmatches 两个表,然后发出nested loop (inner join),这是一个估计有1,176,730,000,000 行的步骤。

其他杂项。资料:

player_idtinyintteambitteam_winbit

match_idbigint primary key,在 players 上有一个外键约束

匹配表

CREATE TABLE [dbo].[Matches](
    [match_id] [bigint] NOT NULL,
    [match_seq_id] [bigint] NOT NULL,
    [team_win] [bit] NOT NULL,
    CONSTRAINT [PK_Matches] PRIMARY KEY CLUSTERED (
        [match_id] ASC
    ) ON [PRIMARY]
) ON [PRIMARY]

玩家表

CREATE TABLE [dbo].[Players] (
    [id]         [int] PRIMARY KEY IDENTITY NOT NULL,
    [match_id]   [bigint] NOT NULL,
    [account_id] [bigint] NOT NULL,
    [team]       [bit] NOT NULL,
    [player_id]  [tinyint] NOT NULL,
    /* column list has been truncated for brevity. */
    CONSTRAINT [PK_Players] PRIMARY KEY CLUSTERED (
        [id] ASC
    ) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Players] ADD CONSTRAINT [FK_Players_Matches] FOREIGN KEY([match_id])
REFERENCES [dbo].[Matches] ([match_id])

【问题讨论】:

  • 您的查询没有按照您的想法执行。请提供playersmatches 表的布局。例如,为什么名为players 的表中会有matchid
  • 你有一个笛卡尔积...在玩家 b 上加入玩家 a,其中 a.playerID b.playerID 正在创建 1700 万 - 1 * 1700 万 - 1 行....可以'不是故意的,可以吗?
  • @GordonLinoff:添加了表格。将球员想象成,更像是“参加特定比赛的球员”。 Matches 中的每个 match_id 大约有 10 个,给予或接受。
  • @Twelfth:这是我想得到我想要的结果集的唯一方法。如果有更好的方法(我很肯定肯定有),那么我需要学习它。
  • 抱歉,请向 gordon 阅读上述评论。所以这应该是每个玩家的赢/输数?

标签: sql sql-server performance database-performance


【解决方案1】:

您缺少对 players 表的连接,因此您得到的是笛卡尔积。试试这个:

SELECT
    b.player_id,
    COUNT(CASE WHEN a.team = m.team_win THEN 0 END),
    COUNT(CASE WHEN a.team <> m.team_win THEN 1 END)
FROM
    matches m
JOIN players a ON m.match_id = a.match_id
JOIN players b ON m.match_id = b.match_id
WHERE
    a.player_id <> b.player_id
    and a.team <> b.team
    and a.player_id = 100
GROUP BY
    b.player_id

【讨论】:

  • +1 看起来不错……他需要从比赛桌开始,而不是从球员开始。
  • 您将 trillion 行嵌套循环转换为秒查询的一小部分。谢谢你。希望我能投票两次。
  • @JHallam - 很高兴能帮上忙!
【解决方案2】:

您似乎想知道:对于每个玩家,该玩家团队赢了多少次,输了多少次,当该玩家与 100 人交手时。我们先来获取团队摘要:

SELECT p.team,
       SUM(CASE WHEN p.team = m.team_win THEN 1 ELSE 0 END) as teamwins,
       SUM(CASE WHEN p.team <> m.team_win THEN 1 ELSE 0 END) as teamlosts
FROM players p JOIN
     matches m
     ON m.match_id = p.match_id
GROUP BY p.team
HAVING sum(case when p.player_id = 100 then 1 else 0 end) -- be sure player 100 played

接下来,您可以加入回队以获取球队信息。

我暂时停在这里,因为我需要确保团队随着时间的推移保持稳定。

【讨论】:

    猜你喜欢
    • 2013-08-23
    • 2021-05-25
    • 2017-09-03
    • 2015-06-29
    • 2018-04-15
    • 1970-01-01
    • 2020-03-09
    • 2020-01-15
    • 1970-01-01
    相关资源
    最近更新 更多