MySQL CONSTRAINT на двух УНИКАЛЬНЫХ КЛЮЧАХ, которые используют одинаковые ИНОСТРАННЫЕ КЛЮЧИ - PullRequest
2 голосов
/ 12 февраля 2012

Я хочу создать таблицу MySQL для хранения данных о взаимоотношениях между пользователями.Отношения между пользователем от A до B и от пользователя B до A могут быть разными.

Пример:

  1. Отношение (с) Боб (к) Алисе:0,9 - Боб любит вещи Алисы.
  2. Отношения (от) Алисы (до) Боб: 0,5 - Алиса находит вещи Боба посредственными.

Мой вопрос:

Iреализовали два ОГРАНИЧЕНИЯ как УНИКАЛЬНЫЕ КЛЮЧИ на двух КЛЮЧЕВЫХ КЛЮЧАХ, которые ссылаются на user_ids в таблице пользователей.Я могу это сделать?Рассматриваются ли они как два отдельных УНИКАЛЬНЫХ КЛЮЧА?

Как реализовать CONSTRAINT, которая будет разрешать только одно вхождение каждого (от) отношения UserA (до) UserB и (от) отношения UserB (до) UserA для user_id?Правильно ли я это делаю?

SQL:

CREATE TABLE relationships (
  relationship_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
  from_user_id MEDIUMINT UNSIGNED NOT NULL,
  to_user_id MEDIUMINT UNSIGNED NOT NULL,
  relationship_level DECIMAL(1,1) NOT NULL,
  PRIMARY KEY (relationship_id),

  FOREIGN KEY (from_user_id) REFERENCES users (user_id)
    ON DELETE CASCADE ON UPDATE NO ACTION,

  FOREIGN KEY (to_user_id) REFERENCES users (user_id)
    ON DELETE CASCADE ON UPDATE NO ACTION,

  CONSTRAINT from_to_relationship UNIQUE KEY (from_user_id, to_user_id),
  CONSTRAINT to_from_relationship UNIQUE KEY (to_user_id, from_user_id),

  INDEX relationship_from_to (relationship_id, from_user_id, to_user_id, relationship_level),
  INDEX relationship_to_from (relationship_id, to_user_id, from_user_id, relationship_level)

) ENGINE=INNODB;

Я надеюсь, что кто-то может помочь.

1 Ответ

2 голосов
/ 12 февраля 2012

Оставьте только одно из этих UNIQUE ограничений - нет необходимости иметь оба. Когда строка терпит неудачу UNIQUE KEY (from_user_id, to_user_id), она также терпит неудачу UNIQUE KEY (to_user_id, from_user_id) и наоборот, поэтому они логически эквивалентны. Даже с одним ограничением UNIQUE при попытке представить отношения между Алисой и Бобом вы можете иметь не более одной строки {Алиса, Боб}, и не более одной строки {Боб, Алиса}.

Что касается производительности (то есть обхода отношений в обоих направлениях), вы можете рассмотреть возможность индексации {from_user_id, to_user_id} (для "прямого" обхода) и / или {to_user_id, from_user_id} (для "обратного" обхода). Вы можете даже отказаться от суррогатного первичного ключа (relationship_id) и перейти к естественному PK, что снизит потребность в индексации (вторичные индексы дороги для кластеризованных таблиц, см. Общие сведения о кластерных индексах InnoDB , раздел «Недостатки» кластеризации ").

На мой взгляд, ваша таблица должна выглядеть так:

CREATE TABLE relationships (
    from_user_id MEDIUMINT UNSIGNED NOT NULL,
    to_user_id MEDIUMINT UNSIGNED NOT NULL,
    relationship_level DECIMAL(1,1) NOT NULL,
    PRIMARY KEY (from_user_id, to_user_id), -- InnoDB is clustered, so naturally "covers" relationship_level as well.
    FOREIGN KEY (from_user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE NO ACTION,
    FOREIGN KEY (to_user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE NO ACTION,
    INDEX relationship_to_from (to_user_id, from_user_id, relationship_level) -- Including relationship_level may or may not be a good idea.
) ENGINE=INNODB;

ПРИМЕЧАНИЕ. Включите ли вы relationship_level в INDEX или нет, зависит от того, хотите ли вы сканирование только по индексу в обратном направлении. «Прямое» направление естественно покрывается PK (поскольку InnoDB кластеризовано ).

...