Связывание 2 идентификаторов в MySQL - PullRequest
1 голос
/ 01 февраля 2012

У меня есть эта таблица

CREATE TABLE IF NOT EXISTS `links` (
  `link_id` int(20) NOT NULL AUTO_INCREMENT,
  `item1_id` int(20) NOT NULL,
  `item2_id` int(20) NOT NULL,
  PRIMARY KEY (`link_id`),
  UNIQUE KEY `item_id` (`item1_id`,`item2_id`)
) ENGINE=InnoDB;

Как я могу ограничить ее, чтобы the item_id мог появляться только один раз в item1_id ИЛИ item2_id?

Поскольку я хочу, чтобы элемент был связан только с одним другим элементом ...

Ответы [ 4 ]

5 голосов
/ 01 февраля 2012

Я хочу, чтобы элемент был связан только с одним другим элементом

Это означает, что вам не нужна таблица ссылок. Вам просто нужен столбец linkedItemId в таблице элементов с уникальным ограничением. Как только Item2 связан с Item1 (Item1ID находится в связанныйItemId для строки Item2), ничто больше не может ссылаться на Item1.

Кроме того, таблица ссылок не нуждается в собственных суррогатных ключах

Отредактируйте, обратите внимание, что MySQL допускает использование нескольких NULL в уникальном индексе (в отличие, скажем, от SQL Server, где вы будете использовать отфильтрованный уникальный индекс для игнорирования NULL)

Из MySQL 5.5 ИНДЕКС СОЗДАНИЯ

Для всех механизмов индекс UNIQUE разрешает несколько значений NULL для столбцов, которые могут содержать NULL

1 голос
/ 01 февраля 2012

... а также вопрос о том, имеют ли ссылки явное направление - то есть отличается ли AB от BA?

I думаю (вам нужно проверить это)что вы можете иметь дело с обоими случаями, используя триггер.Однако я не знаю ни одного метода, с помощью которого вы можете явно выдать ошибку из процедурного языка MySQL.Поскольку вы указали, что столбцы должны иметь значение NOT NULL без значения по умолчанию, и при условии, что NEW доступен для записи, тогда ....

CREATE TRIGGER ins_link BEFORE INSERT on links
FOR EACH ROW
BEGIN
  IF (NEW.item1_id = NEW.item2_id) THEN
      NEW.item2_id=NULL; /* subsequent INSERT will fail */
  END IF
  /* if you want AB=BA.... */
  IF (NEW.item1_id > NEW.item2_id) THEN
     @tempvar=NEW.item1_id;
     NEW.item1_id=NEW.item2_id;
     NEW.item2_id=@tempvar;
  END IF;
END;

(может потребоваться также триггер до обновления, но остаток кодато же самое).

1 голос
/ 01 февраля 2012

Вы можете уменьшить UNIQUE KEY до item1_id.Это означает, что таблица определяет отношение 1:1, а не 1:n.Кроме того, вы можете удалить первичный ключ auto_increment, он не нужен в этих таблицах «ссылок»:

CREATE TABLE IF NOT EXISTS links (
   item1_id int(20) NOT NULL,
   item2_id int(20) NOT NULL,
  PRIMARY KEY (item1_id),
  FOREIGN KEY (item1_id)                 --- I assume you have these 2
    REFERENCES item (item_id),           --- Foreign Keys, too
  FOREIGN KEY (item2_id)
    REFERENCES item (item_id)
) ENGINE=InnoDB;

Разница между ответом этого и @ gbn состоит в том, что это не допускает пустых значений и ненужно, чтобы сохранить элемент, который не связан.Оба дизайна работают почти одинаково, с незначительными изменениями в операторах Insert / Delete / Update.

Однако в обоих проектах мы можем иметь связанные пары, такие как: (1 -> 2), (2 -> 3), (3 -> 7).Если это соответствует требуемой спецификации, оба дизайна хороши.


Если, однако, мы хотим, чтобы элементы отображались только в одной связанной паре, по обе стороны от ссылки сложнее реализовать.

Одним из способов было бы убедиться, что все вставки в таблице links выполняются с помощью процедуры, которая либо вставляет пары (1, 2) и (2, 1), либо завершается ошибкой (и аналогично для операторов Delete / Update, которые будутприходится иметь дело с 2 рядами).

Другие более сложные способы включают в себя триггеры (или экзотические структуры, такие как представления индексов, недоступные в MySQL).

Если вам нужен нормализованный дизайн, есть и такой подход (сложный, но без триггеров).):

CREATE TABLE IF NOT EXISTS link_help (
   item1_id int(20) NOT NULL,
   item2_id int(20) NOT NULL,
  PRIMARY KEY (item1_id),
  FOREIGN KEY (item1_id) 
    REFERENCES item (item_id),  
  FOREIGN KEY (item2_id)
    REFERENCES item (item_id),
  UNIQUE KEY (item1_id, item2_id)          --- this will be needed below
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS links (
   item1_id int(20) NOT NULL,
   item2_id int(20) NOT NULL,
  PRIMARY KEY (item1_id),
  FOREIGN KEY (item1_id, item2_id) 
    REFERENCES link_help (item1_id, item2_id), 
  FOREIGN KEY (item2_id, item1_id)               --- notice the different 
    REFERENCES link_help (item1_id, item2_id)    --- order here
) ENGINE=InnoDB;

Теперь вы не можете добавлять (1 -> 2), (2 -> 3), (3 -> 7) строк в таблицу links.

1 голос
/ 01 февраля 2012

Я не думаю, что INNODB может применить такое ограничение (или MyIsam в этом отношении).Я предлагаю создать хранимую процедуру для обработки вставки.Сначала проверьте ваши пользовательские ограничения, а затем вставьте, как обычно, если нет конфликта.

...