Матрица переменного размера в MySQL - PullRequest
0 голосов
/ 26 апреля 2011


Я создаю приложение, в котором пользователи могут зарегистрироваться, а затем бросить вызов друг другу (любой другой). Когда пользователь 1 бросил вызов пользователю 2, оценка, существующая между ними, обновляется и сохраняется (оценка по умолчанию равна 1 для каждого другого пользователя). Взаимно, пользователь 2 может бросить вызов пользователю 1, и это обновляет другой счет.

Простейшим способом представления этой информации является матрица n * n (с пустой диагональю bcs у вас нет «оценки» против себя). Мой вопрос: как вы храните его с помощью MySQL? Я думал о таблице с 3 столбцами: претендент, вызов, оценка, но это привело бы к размеру n², и такой экспоненциальный фактор кажется неуместным для такого простого запроса. Есть ли другой, предполагаемый способ обработки матриц с MySQL?

Спасибо

Ответы [ 2 ]

0 голосов
/ 26 апреля 2011

Ваша идея стола выглядит хорошо. Если значение по умолчанию равно 1, вам не нужно хранить его в базе данных. Наличие 1000 пользователей без каких-либо проблем дало бы вам таблицу, состоящую из 0 строк, почти не занимая места. Вам не нужно резервировать место, ваш стол такой же большой, как и многие проблемы, которые вы в него ставите. Но если у всех пользователей есть проблемы со всеми остальными пользователями, вам все равно понадобится пространство O (n²), если только у ваших оценок нет специальных правил, позволяющих динамически считать счет на основе других переменных.

0 голосов
/ 26 апреля 2011

Ваша первоначальная идея кажется правильной.
Кстати, n² является квадратичным, а не экспоненциальным.

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;

Если вы делаете небольшой трюк и добавляете следующий триггер, вы можетеforce that player1.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

Надежды, это дает вам представление о том, что возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...