Связи с внешними ключами и «принадлежит многим» - PullRequest
1 голос
/ 07 мая 2010

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

-

У меня есть следующая модель:

S принадлежит T

T имеет много S

A, B, C, D, E (и т. Д.) Имеют по 1 T каждый, поэтому T должен принадлежать каждому из A, B, C, D, E (и т. Д.)

Сначала я настроил свои внешние ключи так, чтобы в A fk_a_t был внешним ключом для At to T (id), в B это было бы fk_b_t и т. Д. В моем UML все выглядит нормально (с использованием MySQLWorkBench) , но генерация моделей yii приводит к мысли, что у T много A, B, C, D (и т. д.), что для меня является обратным.

Для меня это звучит так, как будто мне нужны таблицы A_T, B_T, C_T (и т. Д.), Но это было бы больно, так как есть много таблиц, которые имеют эту связь. Я также подумал, что лучшим способом сделать это было бы какое-то поведение, такое, что A, B, C, D (и т. Д.) Могут вести себя как T, но я не совсем понимаю, как именно это сделать ( Я буду продолжать Google больше об этом)

РЕДАКТИРОВАТЬ - чтобы уточнить, что T может принадлежать только одному из A, или B, или C, (и т. Д.), Но не двум A, ни A и B (то есть это не много ко многим) , Мой вопрос касается того, как описать это отношение в моделях Yii Framework - например, (A, B, C, D, ...) HAS_ONE T, а T принадлежит (A, B, C, D, ..). .). С точки зрения бизнес-использования это все имеет смысл, но я не уверен, правильно ли я настроил его в базе данных, или, если я это сделаю, мне нужно использовать «поведение» в Yii, чтобы оно понимало взаимосвязь , @ rwmnau Я понимаю, что ты имеешь в виду, надеюсь, мои разъяснения помогут.

UML: UML-диаграмма http://www.freeimagehosting.net/uploads/fd8efa2f1d.png

Вот DDL (сгенерированный автоматически). Просто представьте, что существует более 3 таблиц, ссылающихся на T.

-- -----------------------------------------------------
-- Table `mydb`.`T`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`T` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`S`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`S` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `thing` VARCHAR(45) NULL ,
  `t` INT NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_S_T` (`id` ASC) ,
  CONSTRAINT `fk_S_T`
    FOREIGN KEY (`id` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`A`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`A` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff` VARCHAR(45) NULL ,
  `bar` VARCHAR(45) NULL ,
  `foo` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`B`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`B` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff2` VARCHAR(45) NULL ,
  `foobar` VARCHAR(45) NULL ,
  `other` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`C`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`C` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff3` VARCHAR(45) NULL ,
  `foobar2` VARCHAR(45) NULL ,
  `other4` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

Ответы [ 4 ]

1 голос
/ 07 мая 2010

Ваша проблема отчасти в том, что вы не можете отличить, к какой из таблиц она относится.

Кроме того, если вы можете иметь только одну запись, которая соответствует любой из трех или четырех других таблиц, это не нормальное отношение и не может быть смоделировано с использованием обычных методов. Триггер может убедиться, что это правда, но только с столбцом id в нем, что мешает ему сопоставить id в таблице A с 10 и anid в таблице C с 10 (нарушая правила).

Кстати, идентификатор колонки именования обычно плохой выбор для обслуживания. Гораздо понятнее, что происходит, если вы называете столбец с именем таблицы для PK и используете точное имя Pk для FK.

Альтернативное решение для вас - это иметь в средней таблице столбец для каждого типа идентификатора и триггер, чтобы гарантировать, что только у одного из них есть значения, но это затруднительно, если вам нужны все идентификаторы. Составные PK id и idtype могут работать, чтобы гарантировать отсутствие повторений внутри типа, но чтобы вообще не иметь повторений, вам понадобится триггер.

1 голос
/ 08 мая 2010

Это дилемма, которая возникает довольно регулярно, и ИМХО идеального решения не существует.

Однако я бы порекомендовал следующее:

Объедините таблицы S и T. Я не вижу реальной необходимости в таблице T.

Инвертировать отношение таблиц A / B / C к таблице S (ранее T). Под этим я подразумеваю удаление FK на стороне A / B / C и создание обнуляемых столбцов FK на стороне S. Итак, теперь ваша таблица S имеет три дополнительных столбца, допускающих значение NULL: A_ID, B_ID, C_ID.

Создайте проверочное ограничение для таблицы S, гарантируя, что ровно один из этих столбцов всегда имеет значение (или ни один из них не имеет значения, если это разрешено).

Если правилом является ровно одно значение, вы также можете создать уникальное ограничение для этих трех столбцов, чтобы гарантировать, что только один S может быть связан с A / B / C.

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

Обновление после вашего комментария

Хорошо, тогда я забуду об инверсии отношений и оставил бы FK на стороне A / B / C. Я бы по-прежнему применял уникальность использования с помощью проверочного ограничения, но для этого требовалось бы пересекать таблицы, и, вероятно, он будет выглядеть по-разному для каждого варианта SQL (например, SQL Server требует, чтобы UDF просматривал таблицы в проверочном ограничении). Я все еще думаю, что вы можете уничтожить стол T.

Что касается стороны ORM, я вообще не знаю, поэтому не могу об этом говорить. Но если вы реализуете связь на уровне базы данных, то как вы реализуете ее с помощью кода, не должно иметь значения, поскольку база данных отвечает за целостность данных (они просто будут выглядеть как ванильные отношения с ORM). Однако может возникнуть проблема с перехватом конкретной ошибки, которая возникает, если во время выполнения нарушается правило проверочного ограничения.

Я должен также упомянуть, что, если в рассматриваемые таблицы попадает большой (или даже достаточно большой) объем данных, рекомендованный мною подход может быть не лучшим, так как вашему проверочному ограничению придется проверять все 20 таблицы для обеспечения соблюдения правила.

0 голосов
/ 13 мая 2010

Мне пришлось столкнуться с подобной ситуацией несколько недель назад (не моя собственная база данных, я предпочитаю объединять все таблицы в одну, если не в очень специфических ситуациях).
Решение, которое я реализовал, было следующим: в файле модели "T" я сделал что-то вроде этого в функции Relations ():

'id_1' => array(self::BELONGS_TO, 'A', 'id'),
'id_2' => array(self::BELONGS_TO, 'B', 'id'),
'id_3' => array(self::BELONGS_TO, 'C', 'id'),

Надеюсь, это поможет вам.
Привет.

0 голосов
/ 07 мая 2010

Вам понадобится стол посередине только в том случае, если это отношение «многие ко многим», и это не так, так что не беспокойтесь о них.

Ваш вопрос неясен - может ли T принадлежать более 1 A, более 1 B и т. Д.? Или один T принадлежит каждому из A-E, а не другим? Это разница между отношением 1-к-1 (каждый T имеет ровно по одному каждому из AE) и отношением 1-ко-многим (каждый AE имеет ровно 1 T, но T может принадлежать многим A, множеству B и скоро). Имеет ли это смысл?

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

...