MySQL中的可变大小矩阵

时间:2011-04-26 12:55:11

标签: mysql matrix


我正在构建一个应用程序,用户可以在其中注册然后互相挑战(任何其他)。当用户1挑战用户2时,更新并存储它们之间存在的分数(对于每个其他用户,默认分数为1)。相互来说,用户2可以挑战用户1,这会更新另一个分数。

表示此信息的最简单方法是n * n矩阵(空白对角线bcs,你没有对自己的“分数”)。我的问题是:你如何使用MySQL存储它?我想的是一个包含3列的表格:挑战者,挑战者,得分,但这会导致n²大小,这样的指数因素似乎不适合这样一个简单的请求。还有另一种用MySQL处理矩阵的方法吗?

由于

2 个答案:

答案 0 :(得分:0)

你最初的想法似乎是正确的 BTWn²是二次的,而不是指数的。

MySQL应该处理得很好。

CREATE TABLE score (
  id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
  player1_id integer NOT NULL,
  player2_id integer NOT NULL,
  score_p1 integer NOT NULL,
  score_p2 integer NOT NULL,
  chal_date date KEY NOT NULL,
  /*UNIQUE KEY p1p2 (player1_id, player2_id),*/
  FOREIGN KEY p1 (player1_id) REFERENCES player.id 
    ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY p2 (player2_id) REFERENCES player.id 
    ON DELETE CASCADE ON UPDATE CASCADE)
 Engine=InnoDB;

请注意,只有在发生实际挑战并且需要记录分数时才创建一行。因此,每次挑战你只需要一行。如果相同的玩家可以互相挑战,那么一旦你应该删除唯一密钥p1p2。

使用示例

现在,如果你想知道一名球员得分多少,那么

SELECT ifnull(sum(a.score_p1),0) + ifnull(sum(b.score_p2),0) AS total_score 
FROM player
INNER JOIN score a ON (a.player1_id = player.id) 
INNER JOIN score b ON (b.player2_id = player.id)
WHERE player.id = 587;

如果你想知道玩家挑战的所有玩家

SELECT score.player1 as opponent FROM score 
WHERE score.player2 = 587
UNION ALL
SELECT score.player2 as opponent FROM score
WHERE score.player1 = 587;

如果您执行小技巧并添加以下触发器,则可以强制执行此操作 player1.id< player2.id

DELIMITER $$

CREATE TRIGGER bi_score_each BEFORE INSERT ON score FOR EACH ROW
BEGIN
  DECLARE temp integer;

  /*Force player1.id to always be smaller than player2.id*/
  IF new.player1_id > new.player2_id THEN BEGIN
    SET temp = new.player1_id;
    SET new.player1_id = new.player2_id;
    SET new.player2_id = temp;
  END; END IF;
END$$

DELIMITER;

现在,您可以简化查询以查看两个玩家之间的累积分数。

@p1:= 578;
@p2:= 789;

SELECT sum(score.score_p1) as score_p1
  ,sum(score.score_p2) as score_p2
FROM score 
WHERE score.player1_id = if(@p1<@p2,@p1,@p2) 
  AND score.player2.id = if(@p1>@p2,@p1,@p2);

使用以下查询选择玩家与分数对抗的所有玩家

  SELECT score.player2_id as opponent_id
    ,sum(score.score_p2) as opponent_score
    ,sum(score.score_p1) as player_score
    ,p2.name as opponent_name
    ,p1.name as player_name
  FROM score
  INNER JOIN player p2 ON (p2.id = score.player2_id) 
  INNER JOIN player p1 ON (p1.id = score.player1_id)
  WHERE player1_id = 587
  GROUP BY player2.id
UNION ALL
  SELECT score.player1_id as opponent_id
    ,sum(score.score_p1) as opponent_score
    ,sum(score.score_p2) as player_score
    ,p1.name as opponent_name
    ,p2.name as player_name
  FROM score
  INNER JOIN player p2 ON (p2.id = score.player2_id) 
  INNER JOIN player p1 ON (p1.id = score.player1_id)
  WHERE player2_id = 587
  GROUP BY player1.id

希望这可以让你了解可行的方法。

答案 1 :(得分:0)

你的餐桌想法看起来不错。如果默认分数为1,则不必将其存储在数据库中。拥有1000个没有挑战的用户会给你一个由0行组成的表,几乎没有任何空间。你不需要预留空间,你的桌子就像你放入的许多挑战一样大。但是,如果所有用户都遇到了所有其他用户的挑战,除非你的分数有一些特殊的规则,否则你可能需要O(n²)空间,这样就可以根据其他变量动态计算分数。