Я задумался над этим и не смог придумать ничего лучшего.Итак, позвольте мне описать мою проблему, мое текущее решение и то, что я хотел бы улучшить.У меня также есть несколько проблем, например, является ли мой дизайн на самом деле нормализованным или нет.
Я создаю базу данных, где я хотел бы хранить информацию 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-й таблице, поскольку они необходимына уникальном индексе этой таблицы ... чтобы гарантировать, что у игроков только один матч за этап турнира.
Вот мой основной вопрос:
- В настоящее время возможно иметь потерянные строки в первой таблице (совпадения).В матче должно быть ровно два игрока.В частности, если совпадение существует в таблице совпадений, возможно, что в таблице match_players не будет строк, совпадающих с ним.Есть ли способ убедиться, что совпадения ВСЕГДА имеют две связанные строки в match_players?Используя «первый» метод, я определенно ограничил количество игроков в матче менее чем 2 ... так что придумал способ, как минимум 2 игрока решили бы проблему.
Вот одна из моих проблем:
- Поскольку потерянные строки могут существовать до сих пор, есть ли какие-либо другие аномалии данных, которые могут возникнуть в этом проекте?Мне немного не по себе из-за составного (тройного) первичного ключа в match_players, но я думаю, что составное требование foreign_key покрывает меня для этой таблицы.
Спасибо всем, кто может мне помочь.Это лучшее, что я мог сделать до сих пор.Я думаю, что если я решу проблему с потерянными строками, то этот дизайн будет идеальным.Я предполагаю, что могу настроить задание cron, чтобы очистить потерянные строки, но я хотел бы знать, существует ли более чистый дизайн, прежде чем остановиться на этом.
Я думаю, что подзапрос в проверочном ограничениирешит проблему, но, увы, я не думаю, что PostgreSQL на самом деле поддерживает эту функцию.