Отношения Внешнего ключа и “принадлежат многим”

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

--

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

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

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

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

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

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

РЕДАКТИРОВАНИЕ - для разъяснения 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 diagram

Вот 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;

1
задан Glorfindel 25 July 2019 в 17:09
поделиться

4 ответа

Отчасти ваша проблема заключается в том, что у вас нет способа отличить, к какой из таблиц она относится.

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

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

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

1
ответ дан 3 September 2019 в 00:44
поделиться

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

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

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

0
ответ дан 3 September 2019 в 00:44
поделиться

Несколько недель назад мне пришлось столкнуться с похожей ситуацией (не моя собственная база данных, я предпочитаю объединять все таблицы в одну, за исключением очень специфических ситуаций).
Решение, которое я реализовал, было следующим: В файле модели "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
ответ дан 3 September 2019 в 00:44
поделиться

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

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

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

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

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

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

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

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

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

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

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

1
ответ дан 3 September 2019 в 00:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: