Как правильно спроектировать таблицу совпадений VS в PostgreSQL? - PullRequest
4 голосов
/ 18 октября 2011

Я задумался над этим и не смог придумать ничего лучшего.Итак, позвольте мне описать мою проблему, мое текущее решение и то, что я хотел бы улучшить.У меня также есть несколько проблем, например, является ли мой дизайн на самом деле нормализованным или нет.

Я создаю базу данных, где я хотел бы хранить информацию VS Match для турниров.Для простоты, давайте представим, что это шахматные матчи.1х1.Мой текущий дизайн выглядит следующим образом:

CREATE TABLE matches(
  match_id bigserial PRIMARY KEY,
  tournament_id int NOT NULL,
  step int NOT NULL,
  winner match_winner,
  (etc. etc.)
  UNIQUE(match_id, tournament_id, step), -- Actual primary key
  FOREIGN KEY (tournament_id) references tournaments(tournament_id)
    ON DELETE RESTRICT
    ON UPDATE CASCADE
);

CREATE TABLE match_players(
  match_id bigint NOT NULL,
  tournament_id int NOT NULL,
  step int NOT NULL,
  player_id int NOT NULL,
  first boolean NOT NULL,
  PRIMARY KEY (match_id, tournament_id, step, player_id),
  UNIQUE (tournament_id, step, player_id),
  foreign key (match_id, tournament_id, step) -- keep em together
        references matches(match_id, tournament_id, step)
    ON DELETE RESTRICT
    ON UPDATE CASCADE,
  foreign key (player_id) references accounts(player_id)
    ON DELETE RESTRICT
    ON UPDATE CASCADE
);

-- Partial index, ensure no more than one "first" player exists per match
CREATE UNIQUE INDEX idx_match_players_primary
    ON match_players
    USING btree
    (match_id, tournament_id, step)
    WHERE first=true;

-- Also ensure that no more than one "not-first" player exists per match
CREATE UNIQUE INDEX idx_match_players_not_primary
    ON match_players
    USING btree
    (match_id, tournament_id, step)
    WHERE first=false;

Чтобы получить фактические против совпадений, я могу просто присоединить match_players к себе (на mp1.match_id = mp2.match_id и mp1.first = true и mp2.first= false, где mp1 и mp2 - это два совпадения).Частичные уникальные индексы гарантируют, что можно добавить максимум двух игроков.

База данных была нормализована таким образом, потому что игроки неупорядочены.Например, A vs B - это то же самое, что и B против A. Я добавил «первый» логический тип для совпадений, чтобы можно было последовательно отображать A против B.(Полагаю, я мог бы упростить его, чтобы mp1.player_id

Tourin_id и шаг повторяются во 2-й таблице, поскольку они необходимына уникальном индексе этой таблицы ... чтобы гарантировать, что у игроков только один матч за этап турнира.

Вот мой основной вопрос:

  1. В настоящее время возможно иметь потерянные строки в первой таблице (совпадения).В матче должно быть ровно два игрока.В частности, если совпадение существует в таблице совпадений, возможно, что в таблице match_players не будет строк, совпадающих с ним.Есть ли способ убедиться, что совпадения ВСЕГДА имеют две связанные строки в match_players?Используя «первый» метод, я определенно ограничил количество игроков в матче менее чем 2 ... так что придумал способ, как минимум 2 игрока решили бы проблему.

Вот одна из моих проблем:

  • Поскольку потерянные строки могут существовать до сих пор, есть ли какие-либо другие аномалии данных, которые могут возникнуть в этом проекте?Мне немного не по себе из-за составного (тройного) первичного ключа в match_players, но я думаю, что составное требование foreign_key покрывает меня для этой таблицы.

Спасибо всем, кто может мне помочь.Это лучшее, что я мог сделать до сих пор.Я думаю, что если я решу проблему с потерянными строками, то этот дизайн будет идеальным.Я предполагаю, что могу настроить задание cron, чтобы очистить потерянные строки, но я хотел бы знать, существует ли более чистый дизайн, прежде чем остановиться на этом.

Я думаю, что подзапрос в проверочном ограничениирешит проблему, но, увы, я не думаю, что PostgreSQL на самом деле поддерживает эту функцию.

1 Ответ

2 голосов
/ 23 марта 2013

Это то, что я называю «перспективной проблемой», а именно то, что у вас могут возникнуть проблемы, связанные с ограничениями данных, которые зависят от еще не вставленных строк.У транзакции в целом есть требования, которых нет у отдельных строк.Большинство баз данных предлагают несколько инструментов для решения этой проблемы.К счастью, PostgreSQL предлагает вам несколько вариантов.

Денормализованный «буфер ввода» с использованием TOAST

Первый подход заключается в добавлении столбца в совпадения, называемые соответствием типа match_player[].Тогда вы будете хранить множество игроков на матч здесь.Это будет реализовано в таблице match_player с использованием триггера.Это имеет значительные штрафы, хотя с точки зрения развития и предвидения угловых случаев.Я считаю это приемлемым вариантом, но он не идеален.Это позволяет избежать ограничений на будущее, сглаживая таблицу.Однако он может хранить только записи шага 0.Как только люди делают ходы ... это нужно делать, вставляя только в match_players.

Триггеры ограничения

Второй подход заключается в создании триггерной функции, которая запускаетсяодин раз за оператор как INITIALLY DEFERRED DEFERRABLE CONSTRAINT TRIGGER, выполняемый в конце транзакции.Это вытащит системные столбцы из таблицы для поиска вставленных строк, а затем проверит, чтобы убедиться, что совпадения встречаются в другой таблице.Это, вероятно, лучший общий подход к решению проблемы ограничений на будущее.

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