我正在构建一个应用程序,用户可以在其中注册然后互相挑战(任何其他)。当用户1挑战用户2时,更新并存储它们之间存在的分数(对于每个其他用户,默认分数为1)。相互来说,用户2可以挑战用户1,这会更新另一个分数。
表示此信息的最简单方法是n * n矩阵(空白对角线bcs,你没有对自己的“分数”)。我的问题是:你如何使用MySQL存储它?我想的是一个包含3列的表格:挑战者,挑战者,得分,但这会导致n²大小,这样的指数因素似乎不适合这样一个简单的请求。还有另一种用MySQL处理矩阵的方法吗?
由于
答案 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²)空间,这样就可以根据其他变量动态计算分数。