如何选择具有两个特定播放器的数据库中的所有匹配项?

时间:2014-09-09 15:15:37

标签: mysql sql

情况:

比赛有10名球员,每队5名。总共有100名玩家,每场比赛可以在十个位置中的任何一个中拥有任何玩家。我需要提取以下给出的两名球员:

  1. 玩家A和玩家B盟友有多少场比赛?
  2. 他们中有多少人获胜?
  3. 玩家A和玩家B对手有多少场比赛?
  4. 其中有多少球员A获胜?
  5. 我目前的方法(太慢,可能不起作用):

    我制作了三张桌子,“玩家”,“匹配”和“匹配_玩家”,将比赛映射到玩家中。比赛中的“wins_team”列表示哪支球队获胜(0 =球队A,1 =球队B),match_players表中的列“位置”表示球员在阵容中的位置。 0-4在A队,5-9在B队。


    玩家


    player_id
    43179582
    63260623
    31250276
    54829050
    22257854
    etc...
    

    匹配


    match_id     winning_team
    95824317     0
    06236326     0
    02763125     1
    90505482     0
    78544325     1
    etc...
    

    matches_players


    relation_id     match_id     player_id     position
    1               95824317     43179582      1
    2               95824317     63260623      5
    3               06236326     43179582      7
    4               06236326     54829050      0
    5               06236326     22257854      4
    etc...
    

    这是我用于在同一个团队中找到匹配和赢取英雄数量的声明(经过12个小时的冲浪并尝试理解SQL):

    SELECT
    COUNT(*) AS match_count,
    SUM(CASE WHEN team = winning_team THEN 1 ELSE 0 END) AS win_count
    FROM
        (SELECT
        matches.match_id,
        (CASE WHEN position < 5 THEN 0 ELSE 1 END) AS team,
        winning_team
        FROM matches INNER JOIN matches_players ON matches.match_id = matches_players.match_id
        WHERE player_id = [____] OR player_id = [____]
        GROUP BY match_id, team HAVING COUNT(*) = 2)
    AS pair_matches
    

    它似乎有效,但速度非常慢(250,000场比赛中90秒以上),理想情况下我希望在同一个查询中有相反的团队成绩(这应该比两个单独的查询更快,不是吗?)可以查询变得更快/更好?是否存在数据库设计缺陷?

    我非常感谢任何有帮助的人。欢迎任何地区的建议。感谢。

2 个答案:

答案 0 :(得分:1)

我认为应该这样做或至少给你一个提示

-- How many matches were player A and player B allies?
select DISTINCT (match_id) from MATCHES_PLAYERS where PLAYER_ID='43179582' or PLAYER_ID='63260623' group by MATCH_ID;

-- How many of these did they win?
select  PLAYER_ID, count(WINNING_TEAM)
from 
(select  PLAYER_ID, WINNING_TEAM, POsition from MATCHES_PLAYERS mp, matches m where mp.MATCH_ID=m.MATCH_ID and position<=4 and WINNING_TEAM=0
union all
select PLAYER_ID, WINNING_TEAM, POsition from MATCHES_PLAYERS mp, matches m where mp.MATCH_ID=m.MATCH_ID and position>4 and WINNING_TEAM=1)
group by PLAYER_id;

-- How many matches were player A and player B opponents?
select mp1.*, m.WINNING_TEAM, mp2.PLAYER_ID as opponent from MATCHES_PLAYERS mp1, MATCHES_PLAYERS mp2, matches m where mp1.MATCH_ID=m.MATCH_ID and  mp1.MATCH_ID=mp2.MATCH_ID and mp1.PLAYER_ID!=mp2.PLAYER_ID and (mp1.PLAYER_id in (43179582, 63260623) and mp2.player_id in (43179582, 63260623))
select DISTINCT mp1.MATCH_ID from MATCHES_PLAYERS mp1, MATCHES_PLAYERS mp2, matches m where mp1.MATCH_ID=m.MATCH_ID and  mp1.MATCH_ID=mp2.MATCH_ID and mp1.PLAYER_ID!=mp2.PLAYER_ID and (mp1.PLAYER_id in (43179582, 63260623) and mp2.player_id in (43179582, 63260623))

-- How many of these did player A win?
select
mp1.*, m.WINNING_TEAM, mp2.PLAYER_ID as opponent
from MATCHES_PLAYERS mp1, MATCHES_PLAYERS mp2, matches m
where mp1.MATCH_ID=m.MATCH_ID
and mp1.MATCH_ID=mp2.MATCH_ID
and mp1.PLAYER_ID!=mp2.PLAYER_ID
and
(
   mp1.PLAYER_id=43179582
   and mp2.player_id=63260623
)
and mp1.position<=4
and m.WINNING_TEAM=0

答案 1 :(得分:0)

我认为接受的答案中有错误(在“玩家 A 和 B 的盟友有多少场比赛?”部分)

我的回答使用子查询和 ROUND(position/4) 从位置 0..3 转换为团队 0,从位置 4..7 转换为团队 1。

首先,此脚本创建表(Oracle SQL 风格)。索引仅在主键上自动创建:

CREATE TABLE players(player_id NUMBER PRIMARY KEY);
CREATE TABLE matches(match_id NUMBER PRIMARY KEY, winning_team NUMBER(1) CHECK(winning_team IN (0, 1)));
CREATE TABLE matches_players(relation_id NUMBER PRIMARY KEY, match_id NUMBER NOT NULL REFERENCES matches(match_id), player_id NUMBER NOT NULL REFERENCES players(player_id), position NUMBER NOT NULL CHECK(position < 8));

让我们加载一些数据:

INSERT INTO players
SELECT 43179582 FROM DUAL
UNION ALL SELECT 63260623 FROM DUAL
UNION ALL SELECT 31250276 FROM DUAL
UNION ALL SELECT 54829050 FROM DUAL
UNION ALL SELECT 22257854 FROM DUAL
;

-- match_id     winning_team
INSERT INTO matches
SELECT 95824317, 0 FROM DUAL
UNION ALL SELECT 06236326, 0 FROM DUAL
UNION ALL SELECT 02763125, 1 FROM DUAL
UNION ALL SELECT 90505482, 0 FROM DUAL
UNION ALL SELECT 78544325, 1 FROM DUAL
;

-- relation_id     match_id     player_id     position
DELETE matches_players;
INSERT INTO matches_players
SELECT 1, 95824317, 43179582, 1 FROM DUAL
UNION ALL SELECT 2, 95824317, 63260623, 5 FROM DUAL
UNION ALL SELECT 3, 06236326, 43179582, 7 FROM DUAL
UNION ALL SELECT 4, 06236326, 54829050, 0 FROM DUAL
UNION ALL SELECT 5, 06236326, 22257854, 4 FROM DUAL
UNION ALL SELECT 6, 06236326, 63260623, 4 FROM DUAL
;

然后,这些是查询:

-- How many matches were player A and player B allies?
SELECT COUNT(*) FROM matches_players mp WHERE player_id = 63260623
AND (match_id, ROUND(position/4)) IN (
    SELECT match_id, ROUND(position/4) FROM matches_players WHERE player_id = 22257854
);

-- How many of these did they win?
SELECT COUNT(*) FROM matches_players mp WHERE player_id = 63260623
AND (match_id, ROUND(position/4)) IN (
    SELECT match_id, ROUND(position/4) FROM matches_players WHERE player_id = 22257854
)
AND EXISTS(SELECT NULL FROM matches m WHERE m.match_id = mp.match_id AND m.winning_team = ROUND(mp.position/4))
;

-- How many matches were player A and player B opponents?
SELECT COUNT(*) FROM matches_players mp WHERE player_id = 63260623
AND (match_id, ROUND(position/4)) IN (
    SELECT match_id, -(ROUND(position/4) - 1) FROM matches_players WHERE player_id = 54829050
);

-- How many of these did player A win?
SELECT COUNT(*) FROM matches_players mp WHERE player_id = 54829050
AND (match_id, ROUND(position/4)) IN (
    SELECT match_id, -(ROUND(position/4) - 1) FROM matches_players WHERE player_id = 63260623
)
AND EXISTS(SELECT NULL FROM matches m WHERE m.match_id = mp.match_id AND m.winning_team = ROUND(mp.position/4))
;

如果你想加快这些查询的速度,你可以在 position 和 Winning_team 上创建索引:

CREATE INDEX matches_idx ON matches(winning_team);
CREATE INDEX matches_players_idx ON matches_players(ROUND(position/4));

如果你想要超高速并且你的数据库引擎支持位图索引,你可以尝试:

CREATE BITMAP INDEX matches_idx ON matches(winning_team);
CREATE BITMAP INDEX matches_players_idx ON matches_players(ROUND(position/4));