PostgreSQL: ограничение не повторяющаяся ячейка в строке - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть стол lineup.

CREATE TABLE IF NOT EXISTS lineup (
  match_id    INTEGER REFERENCES matches,
  pos_1       INTEGER REFERENCES players,
  pos_2       INTEGER REFERENCES players,
  pos_3       INTEGER REFERENCES players,
  pos_4       INTEGER REFERENCES players,
  pos_5       INTEGER REFERENCES players,
  pos_6       INTEGER REFERENCES players
);

Это для хранения состава для волейбольного матча.У вас есть шесть позиций на поле.На каждой позиции должен быть игрок.Однако все идентификаторы игроков должны быть разными.Игрок не может находиться на нескольких позициях одновременно.Как я могу смоделировать это, используя ограничение?

match_id, pos_1, pos_2, pos_3, pos_4, pos_5, pos_6
1, 10, 11, 12, 13, 14, 15 // ok
1, 10, 10, 12, 13, 14, 15 // not ok, pos_1 == pos_2
1, 10, 11, 12, 13, 14, 10 // not ok, pos_1 == pos_6
1, 10, 11, 12, 13, 14, 14 // not ok, pos_5 == pos_6

Я думал о чем-то вроде

CONSTRAINT no_duplicate_players CHECK (pos_1 != pos_2 AND pos_1 != pos_3 ... AND pos_5 != pos_6)

Это будет довольно длительное ограничение, и мне интересно, есть ли что-то попроще.

Спасибо, Мирко

Ответы [ 4 ]

0 голосов
/ 27 декабря 2018

Импалер имеет правильную идею, но вы также хотите гарантировать, что на всех шести позициях есть игроки.Для этого вам также понадобятся ограничения NOT NULL:

CREATE TABLE IF NOT EXISTS lineup (
  match_id    INTEGER REFERENCES matches,
  pos_1       INTEGER NOT NULL REFERENCES players,
  pos_2       INTEGER NOT NULL REFERENCES players,
  pos_3       INTEGER NOT NULL REFERENCES players,
  pos_4       INTEGER NOT NULL REFERENCES players,
  pos_5       INTEGER NOT NULL REFERENCES players,
  pos_6       INTEGER NOT NULL REFERENCES players,
  constraint chk_linup_pos
    check (pos_2 not in (pos_1) and
           pos_3 not in (pos_1, pos_2) and
           pos_4 not in (pos_1, pos_2, pos_3) and
           pos_5 not in (pos_1, pos_2, pos_3, pos_4) and
           pos_6 not in (pos_1, pos_2, pos_3, pos_4, pos_5) 
          );
);

Несмотря на то, что вы можете нормализовать структуру данных, на самом деле очень сложно иметь фиксированное ограничение, такое как «все шесть позиций должны быть заполнены»когда значения охватывают несколько строк.Если вы действительно хотите реализовать это, то эта структура, вероятно, является самым простым механизмом.

0 голосов
/ 27 декабря 2018

Одним из способов сделать это эффективно является дальнейшая нормализация таблицы путем нормализации столбца позиции.

Нормализованная таблица

CREATE TABLE IF NOT EXISTS lineup (
    match_id    INTEGER NOT NULL REFERENCES matches,
    position_id INTEGER NOT NULL REFERENCES positions,
    player_id   INTEGER NOT NULL REFERENCES players
    CONSTRAINT lineups_pk PRIMARY KEY (match_id, position_id, player_id)
);

Таблица позиций

Вы такженужна таблица позиций, что-то вроде:

CREATE TABLE IF NOT EXISTS positions (
    position_id INTEGER,
    position TEXT, -- or whatever your character type of choice is
    CONSTRAINT positions_pk PRIMARY KEY (position_id)
);

Этот подход имеет то преимущество, что обеспечивает соблюдение ограничения, которое вы хотите применить, и дополнительное преимущество в том, что он будет гибким в будущем, если вы определите себянеобходимо добавить дополнительную информацию о позициях, игроках или матчах.

Просмотр данных

Затем вы можете представить данные в нужном формате, выполнив что-то вроде этого:

SELECT
      match_id
    , a.player_id AS pos_1
    , b.player_id AS pos_2
    , c.player_id AS pos_3
    , d.player_id AS pos_4
    , e.player_id AS pos_5
    , f.player_id AS pos_6
FROM (SELECT match_id, player_id FROM lineup WHERE position_id = 1) a
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 2) b USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 3) c USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 4) d USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 5) e USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 6) f USING (match_id);

Это немного сложнее, чтобы настроить его таким образом, но как только у вас есть запрос, вы можете создать его как представление, а затем вы всегда получите его, и вы получите выгоду отдополнительная нормализация.Этот способ также облегчает ответы на такие вопросы, как "во сколько матчей играл player_id 1?"и «они всегда играют в одной и той же позиции?»


Обеспечение заполнения всех шести позиций

Первоначально в этом ответе не было требования о том, что все шесть позиций должны быть заполнены,Без дополнений это гарантирует, что каждая позиция перечислимая заполнена, но допускает возможность того, что позиция не будет вставлена.Есть два способа решения этой проблемы:

  • Убедитесь, что на вставке с помощью метода нанесения / вставки

    • Плюсы: быстрее, легче
    • Минусы: не применяется на уровне базы данных, возможно отсутствие данных
  • При вставке / удалении с помощью триггера базы данных

    • Плюсы: обеспечивает на уровне базы данных, делает невозможным (если реализовано правильно) отсутствие пропусков
    • Минусы: здесь будут триггеры

DBA StackExchange имеет отличный вопрос и ответ о том, как реализовать такого рода ограничения с использованием триггеров.

0 голосов
/ 27 декабря 2018

Чтобы сократить длину запроса, используйте NOT IN

    CONSTRAINT no_duplicate_players
    CHECK (pos_1 NOT IN(pos_2,pos_3,pos_4,pos_5)) -- n --similar for each column

, иначе каждый pos1..n будет лучше использовать все внешние ключи (аналогично таблице фактов в схеме хранилища данных), так что они будутобратитесь к основной таблице.

0 голосов
/ 27 декабря 2018

Простое ограничение будет делать:

CREATE TABLE IF NOT EXISTS lineup (
  match_id    INTEGER REFERENCES matches,
  pos_1       INTEGER REFERENCES players,
  pos_2       INTEGER REFERENCES players,
  pos_3       INTEGER REFERENCES players,
  pos_4       INTEGER REFERENCES players,
  pos_5       INTEGER REFERENCES players,
  pos_6       INTEGER REFERENCES players,
  constraint chk1
    check (pos_1 <> pos_2
       and pos_1 <> pos_3
       and pos_1 <> pos_4
       and pos_1 <> pos_5
       and pos_1 <> pos_6
       and pos_2 <> pos_3
       and pos_2 <> pos_4
       and pos_2 <> pos_5
       and pos_2 <> pos_6
       and pos_3 <> pos_4
       and pos_3 <> pos_5
       and pos_3 <> pos_6
       and pos_4 <> pos_5
       and pos_4 <> pos_6
       and pos_5 <> pos_6             
    )
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...