附加联接

时间:2014-10-17 02:14:19

标签: mysql join mysqli

我一直在开设一个显示评级阶梯的网站,它会处理玩家所玩的所有比赛。

我目前所拥有的是下表,但是当我想选择玩家的所有匹配时,我必须包含“或”语句,并且还具有所有具有玩家名称的列的索引。所以我设计了一种新方法。

[当前方法]

aoe3_sp_games表

信息:保留所有比赛以及与之相关的球员。

Column  Type    Null    Default
game_id char(36)    No          
map varchar(30) No          
date_time   int(11) No          
length  float   No          
player_count    tinyint(4)  No          
t1_p1   varchar(16) No          
t1_p2   varchar(16) No          
t1_p3   varchar(16) No          
t1_p4   varchar(16) No          
t2_p1   varchar(16) No          
t2_p2   varchar(16) No          
t2_p3   varchar(16) No          
t2_p4   varchar(16) No          
t1_p1_rating    smallint(4) No          
t1_p2_rating    smallint(4) No          
t1_p3_rating    smallint(4) No          
t1_p4_rating    smallint(4) No          
t2_p1_rating    smallint(4) No          
t2_p2_rating    smallint(4) No          
t2_p3_rating    smallint(4) No          
t2_p4_rating    smallint(4) No          
t1_p1_pr    float(6,2)  No          
t1_p2_pr    float(6,2)  No          
t1_p3_pr    float(6,2)  No          
t1_p4_pr    float(6,2)  No          
t2_p1_pr    float(6,2)  No          
t2_p2_pr    float(6,2)  No          
t2_p3_pr    float(6,2)  No          
t2_p4_pr    float(6,2)  No          
t1_p1_civ   char(2) No          
t1_p2_civ   char(2) No          
t1_p3_civ   char(2) No          
t1_p4_civ   char(2) No          
t2_p1_civ   char(2) No          
t2_p2_civ   char(2) No          
t2_p3_civ   char(2) No          
t2_p4_civ   char(2) No  

[新方法]

aoe3_sp_matches表

信息:保留所有比赛。

Column  Type    Null    Default
match_id    int(10) No          
match_guid  char(36)    No          
map varchar(30) No          
date_time   int(11) No          
length  float   No          
player_count    tinyint(4)  No

aoe3_sp_match_players表

信息:保留与#Matches_table相关的所有与比赛相关的球员。

Column  Type    Null    Default Links to
match_id    int(10) No      aoe3_sp_matches -> match_id     
player_id   int(10) No      eso_players -> player_id        
team    tinyint(4)  No              
player_name varchar(16) No              
player_rating   smallint(4) No              
player_pr   float(6,2)  Yes NULL            
player_civ  char(2) No  

我需要的是使用#Match_Players_table获取玩家所玩的所有比赛。 然后将比赛ID加入参与比赛的球员。

任何比赛最多只能有8名玩家(4vs4)和至少2名(1vs1)玩家。

我正在使用Player 1570进行测试,因为他看起来很适合没有太多比赛的情况。

我已经阅读过某个地方,不要使用不超过2个连接,否则它可能会对性能造成不良影响。 我的网站处理大约5000场比赛3天,17000行处理玩家,这些数字基于新方法。

我的mysql版本是:5.1.61。

我的网站 http://exciple.com/forum/player.php?n=yosimasa&s=m

SQL小提琴 http://sqlfiddle.com/#!2/0b2245/3


结果类型

基本上将一行中属于该比赛的所有球员与比赛信息相结合,就像方法一一样。我确实试图得到这样的结果,但我努力加入不止一个连接的连接。我知道这需要很多连接,所以不是理想的方法。

game_id map date_time   length  player_count    t1_p1   t1_p2   t1_p3   t1_p4   t2_p1   t2_p2   t2_p3   t2_p4   t1_p1_rating    t1_p2_rating    t1_p3_rating    t1_p4_rating    t2_p1_rating    t2_p2_rating    t2_p3_rating    t2_p4_rating    t1_p1_pr    t1_p2_pr    t1_p3_pr    t1_p4_pr    t2_p1_pr    t2_p2_pr    t2_p3_pr    t2_p4_pr    t1_p1_civ   t1_p2_civ   t1_p3_civ   t1_p4_civ   t2_p1_civ   t2_p2_civ   t2_p3_civ   t2_p4_civ
    43f9499f-870d-47c0-8a05-47554e349698    great plains    1413510540  1451    4   greenandugly    Jomp17          Armykid913  FUHAHAHA            1616    1616    0   0   1584    1571    0   0   0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    FR  OT          OT  BR  

结果类型二

所以我一直在搞乱查询,这是我到目前为止所做的。

执行两个查询

[游戏一个]

SELECT aoe3_sp_matches.* FROM aoe3_sp_matches
right JOIN `aoe3_sp_match_players`
ON `aoe3_sp_match_players`.`match_id`=`aoe3_sp_matches`.`match_id`
WHERE player_id = 1570

返回播放器1570的所有游戏。这样可以正常工作。

[第二场比赛的球员]

SELECT aoe3_sp_match_players.* FROM aoe3_sp_match_players
right JOIN `aoe3_sp_matches`
ON `aoe3_sp_match_players`.`match_id`=`aoe3_sp_matches`.`match_id`
WHERE aoe3_sp_match_players.player_id = 1570

返回符合where子句的aoe3_sp_match_players中的数据。不返回所有玩家,只返回where子句中的一个。

SELECT aoe3_sp_match_players.* FROM aoe3_sp_match_players
WHERE aoe3_sp_match_players.match_id in 
    (SELECT aoe3_sp_match_players.match_id
        FROM  aoe3_sp_match_players
        WHERE player_id = 1570)

我设法以我想要的方式获取数据但是使用子查询并且它比我的数据库中的连接慢,而不是在那里出现。

编辑: 我找到了一种方法,可以在外部使用连接获取结果类型2的第二部分,它与放在之前的连接一样快子句。

Select
  aoe3_sp_match_players.match_id,
  aoe3_sp_match_players.team,
  aoe3_sp_match_players.player_name,
  aoe3_sp_match_players.player_rating,
  aoe3_sp_match_players.player_pr,
  aoe3_sp_match_players.player_civ
From
  (Select
    aoe3_sp_match_players.match_id
  From
    aoe3_sp_match_players
  Where
    aoe3_sp_match_players.player_id = 1570
  Limit 30) aoe3_sp_matches Left Join
  aoe3_sp_match_players On aoe3_sp_match_players.match_id =
    aoe3_sp_matches.match_id
Order By
  aoe3_sp_match_players.match_id Desc

您认为获得结果的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

此设置存在问题。你的新方法肯定比上一个好,但它仍然需要一些解决方法。你虽然走在了正确的轨道上。

每场比赛允许的球员数量是否有限制?看起来你的旧方法允许每场比赛最多4名球员。在你的新方法中,你可以在每场比赛中拥有任意数量的球员,这可能是也可能不是你想要的。

只有在您的应用程序中强制执行最多四名玩家才能分享匹配项时,才能实现您期望的结果。但这并不合适,因为您的数据完整性现在取决于应用程序层而不仅仅是数据库。

如果不考虑我提到的所有这些事情,你可以这样做:

SELECT
    game.match_id,
    game.match_guid,
    game.map,
    game.date_time,
    game.length,
    game.player_count,

    team1_player1.player_name,
    team1_player1.player_rating,
    team1_player1.player_pr,
    team1_player1.player_civ,
    team1_player2.player_name,
    team1_player2.player_rating,
    team1_player2.player_pr,
    team1_player2.player_civ,
    team1_player3.player_name,
    team1_player3.player_rating,
    team1_player3.player_pr,
    team1_player3.player_civ,
    team1_player4.player_name,
    team1_player4.player_rating,
    team1_player4.player_pr,
    team1_player4.player_civ,
    team2_player1.player_name,
    team2_player1.player_rating,
    team2_player1.player_pr,
    team2_player1.player_civ,
    team2_player2.player_name,
    team2_player2.player_rating,
    team2_player2.player_pr,
    team2_player2.player_civ,
    team2_player3.player_name,
    team2_player3.player_rating,
    team2_player3.player_pr,
    team2_player3.player_civ,
    team2_player4.player_name,
    team2_player4.player_rating,
    team2_player4.player_pr,
    team2_player4.player_civ
FROM 
    matches_table as game
    LEFT JOIN match_players as team1_player1 ON game.match_id = team1_player1.match_id and team1_player1.player_id = 1
    LEFT JOIN match_players as team1_player2 ON game.match_id = team1_player2.match_id and team1_player1.player_id = 2
    LEFT JOIN match_players as team1_player3 ON game.match_id = team1_player3.match_id and team1_player1.player_id = 3
    LEFT JOIN match_players as team1_player4 ON game.match_id = team1_player4.match_id and team1_player1.player_id = 4
    LEFT JOIN match_players as team2_player1 ON game.match_id = team2_player1.match_id and team1_player1.player_id = 5
    LEFT JOIN match_players as team2_player2 ON game.match_id = team2_player2.match_id and team1_player1.player_id = 6
    LEFT JOIN match_players as team2_player3 ON game.match_id = team2_player3.match_id and team1_player1.player_id = 7
    LEFT JOIN match_players as team2_player4 ON game.match_id = team2_player4.match_id and team1_player1.player_id = 8

你必须先了解球员的ids。此外,玩家只能连接到一场比赛。对于新的比赛,这个值必须改变,我们会放弃玩家所处的任何先前比赛的历史。这是为了说明它可以完成,以及它可以获得多么丑陋。我不建议以这种方式进行。

请注意,您可以多次加入同一张桌子。这可能非常有用,因为您需要将许多记录中的信息压缩为一个。


<强> SOLUTION:

你可以有一个游戏桌,一个玩家桌子,并为比赛创建第三个桌子:

<强>游戏

game_id
map
//other columns with info about this map. you could also name the table maps

<强>球员

player_id
player_name
player_pr
player_civ

<强>匹配

match_id
map
date_time
team1_player1
team1_player2
team2_player1
team2_player2

这最后一个表包含了玩家的ID。输出可能如下所示:

match_id  map              team1_player1  team1_player2  team2_player1  team2_player2
1         great plains     0001           0002           0003           0004

您还可以将用户名用作ID,只需确保它们是唯一的(主键)

生成的SQL查询将是这样的:

SELECT
    games.map,
    matches.match_id,
    matches.date_time,

    team1_player1.player_name,
    team1_player1.player_rating,
    team1_player1.player_pr,
    team1_player1.player_civ,
    team1_player2.player_name,
    team1_player2.player_rating,
    team1_player2.player_pr,
    team1_player2.player_civ,
    team1_player3.player_name,
    team1_player3.player_rating,
    team1_player3.player_pr,
    team1_player3.player_civ,
    team1_player4.player_name,
    team1_player4.player_rating,
    team1_player4.player_pr,
    team1_player4.player_civ,
    team2_player1.player_name,
    team2_player1.player_rating,
    team2_player1.player_pr,
    team2_player1.player_civ,
    team2_player2.player_name,
    team2_player2.player_rating,
    team2_player2.player_pr,
    team2_player2.player_civ,
    team2_player3.player_name,
    team2_player3.player_rating,
    team2_player3.player_pr,
    team2_player3.player_civ,
    team2_player4.player_name,
    team2_player4.player_rating,
    team2_player4.player_pr,
    team2_player4.player_civ
FROM 
    games
    INNER JOIN matches ON matches.map = games.map
    INNER JOIN match_players as team1_player1 ON team1_player1.player_id = matches.team1_player1
    INNER JOIN match_players as team1_player2 ON team1_player1.player_id = matches.team1_player2
    INNER JOIN match_players as team1_player3 ON team1_player1.player_id = matches.team1_player3
    INNER JOIN match_players as team1_player4 ON team1_player1.player_id = matches.team1_player4
    INNER JOIN match_players as team2_player1 ON team1_player1.player_id = matches.team2_player1
    INNER JOIN match_players as team2_player2 ON team1_player1.player_id = matches.team2_player2
    INNER JOIN match_players as team2_player3 ON team1_player1.player_id = matches.team2_player3
    INNER JOIN match_players as team2_player4 ON team1_player1.player_id = matches.team2_player4

通过这种方法,您的玩家表格仅包含有关玩家的信息,而不包含有关匹配的信息。您可以保留玩家所处的每场比赛的历史记录,并且您还可以将与比赛相关的地图和其他信息与比赛本身分开。匹配表只将所有内容联系在一起。此外,玩家编号限制返回到数据库并且与应用程序无关。

希望它有所帮助。