2010-05-07 6 views
1

edit - Basé sur les réponses ci-dessous, je vais revoir mon design. Je pense que je peux éviter ce gâchis en étant un peu plus intelligent avec la façon dont je définis mes objets et règles métier. Merci à tous pour votre aide!Relations avec les clés étrangères et "appartient à beaucoup"

-

I ont le modèle suivant:

S appartient à T

T a beaucoup S

A, B, C, D, E (etc) ont une Tout d'abord, je configure mes clés étrangères de sorte que dans A, fk_a_t soit la clé étrangère sur At to T (id), dans B ce serait fk_b_t, etc. La chose semble bien dans mon UML (en utilisant MySQLWorkBench), mais en générant les modèles yii, on pense que T a beaucoup de A, B, C, D (etc) ce qui pour moi est l'inverse.

Il me semble que j'ai besoin de tables A_T, B_T, C_T (etc), mais ce serait pénible car il y a beaucoup de tables qui ont cette relation. J'ai aussi googlé que la meilleure façon de le faire serait une sorte de comportement, tel que A, B, C, D (etc) peut se comporter comme un T, mais je ne sais pas exactement comment faire cela (Je vais continuer à google plus sur ceci)

EDIT - pour clarifier, un T peut appartenir seulement à un de A, ou B, ou C, (etc) et pas deux A, ni A et un B (que c'est, ce n'est pas beaucoup à beaucoup). Ma question concerne la façon de décrire cette relation dans les modèles Yii Framework - par exemple, (A, B, C, D, ...) HAS_ONE T, et T appartient à (A, B, C, D, .. .). D'un cas d'utilisation métier, tout cela a du sens, mais je ne suis pas sûr si je l'ai correctement configuré dans la base de données, ou si je le fais, que j'ai besoin d'utiliser un "comportement" dans Yii pour lui faire comprendre la relation . @rwmnau Je comprends ce que vous voulez dire, j'espère que ma clarification aide.

UML: uml diagram http://www.freeimagehosting.net/uploads/fd8efa2f1d.png

est ici le DDL (généré automatiquement). Il suffit de faire semblant qu'il n'y a plus de 3 tables faisant référence à 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; 
+0

Pouvez-vous poster le diagramme et/ou le DDL? Ce serait bien de voir à quoi ça ressemble. –

+3

Un peu trop abstrait pour moi. Pouvez-vous publier des structures de tables d'échantillons? – Oded

+0

J'ai ajouté des diagrammes et DDL, et j'espère clarifié ma question. – jan

Répondre

0

Vous avez besoin que d'une table au milieu si elle est un grand nombre à plusieurs, et il ne semble pas que ce soit le cas, alors ne ne vous inquiétez pas pour ceux-ci.

Votre question n'est pas claire - un T peut-il appartenir à plus de 1 A, plus de 1 B, et ainsi de suite? Ou est-ce qu'un seul T appartient à chacun des A-E, et à aucun autre? C'est la différence entre une relation 1-à-1 (chaque T a exactement une AE) et une relation 1-à-plusieurs (chaque AE a exactement 1 T, mais un T peut appartenir à beaucoup de A, beaucoup de B, et bientôt). Est-ce que ça a du sens?

En outre, je voudrais appuyer la demande pour plus d'informations dans votre question pour aider à solidifier ce que vous demandez.

+0

J'ai édité pour répondre à votre question, je l'espère, je ne peux pas appartenir à plus de 1 A, B, C, etc. Un seul T appartient à A-E, et pas d'autres. Quand j'ai créé le modèle automatiquement avec Yii, il les a fait HAS_MANY (ce qui est bien, assez facile à changer en HAS_ONE), mais j'ai remarqué le BELONGS_TO dans T (il a mis BELONGS_TO au dernier des modèles qui l'utilisent) et je suis curieux de savoir si je vais avoir des ennuis en ayant autant de FK pointant vers différentes tables. – jan

+0

@Jan - vous avez raison de dire que le HAS_MANY hébergera HAS_ONE, ce qui semble être vraiment ce que vous voulez. Avoir tant de clés étrangères n'est pas un problème en soi, sauf que cela complique le processus d'ajout de nouvelles lignes, car elles doivent être faites dans cet ordre particulier (et assurez-vous que, si votre SGBDR supporte les "suppressions en cascade" Soyez très prudent avec eux, car ils vont dévaster la base de données quand elle est étroitement liée.Tant que vos clés étrangères ne sont pas circulaires, tout ira bien. – SqlRyan

1

Votre problème est en partie que vous n'avez aucun moyen de distinguer laquelle des tables il est en relation. En outre, si vous ne pouvez avoir qu'un seul enregistrement correspondant à l'une des trois ou quatre autres tables, il ne s'agit pas d'une relation normale et il ne peut pas être modélisé à l'aide de techniques normales. Un déclencheur peut garantir que ceci est vrai mais avec seulement la colonne d'identifiant qui l'empêche de faire correspondre un identifiant dans la table A de 10 et anid dans la table C de 10 (en violant les règles).

L'identification des colonnes de nommage BTW est généralement un mauvais choix pour la maintenance. Il est beaucoup plus clair ce qui se passe si vous nommez la colonne avec le nom de la table pour PK et utilisez le nom exact du PK pour les FK. Une solution alternative pour vous est d'avoir dans la table du milieu une colonne pour chaque type d'identifiant et un déclencheur pour s'assurer que seul l'un d'entre eux a des valeurs, mais c'est une douleur à interroger si vous avez besoin de tous les identifiants. Un PK composé de id et idtype pourrait fonctionner pour s'assurer qu'il n'y a pas de répétitions dans un type, mais pour ne pas avoir de répétitions du tout, vous aurez besoin d'un trigger.

+0

Vous avez raison, il n'y a aucun moyen de regarder à T et déterminer à qui il appartient sans d'abord savoir si vous regardez l'un des (A, B, C ..) Cependant, ces modèles (A, B, C) sont assez différents les uns des autres que Je vais toujours travailler à partir d'un A, B, etc à un T. En tant que tel, la règle de savoir si un T ne peut pas appartenir à un A et B simultanément est moins important qu'un T ne peut pas appartenir à plus de 1 A simultanément (ie, une autre contrainte sur A que son T soit unique) Pour la convention de nommage, je l'ai gardé bref pour la simplicité. Merci pour le conseil si. :) – jan

1

Ceci est un dilemme qui revient assez régulièrement, et il n'y a pas de solution parfaite à mon humble avis.

Cependant, je recommande ce qui suit:

Combine la table S et T. Je ne vois aucun besoin réel pour la table T.

Inversez la façon dont les tables A/B/C se rapportent à la table S (anciennement T). Par ceci je veux dire enlever le FK du côté A/B/C et créer des colonnes FK nullables du côté S. Alors maintenant votre table S a trois colonnes nullables supplémentaires: A_ID, B_ID, C_ID.

Créez une contrainte de vérification sur la table S, en vérifiant que l'une de ces colonnes a toujours une valeur (ou qu'aucune d'entre elles n'a de valeur si cela est autorisé).

Si la valeur d'une seule valeur est la règle, vous pouvez également créer une contrainte unique sur ces trois colonnes pour garantir qu'un seul S peut être associé à un A/B/C.

Si aucune valeur dans l'une de ces colonnes n'est autorisée, la règle ci-dessus devra également être appliquée avec une contrainte de vérification.

Mise à jour après votre commentaire

Ok, alors j'oublier inverser les relations et maintenir le FK sur le côté A/B/C. J'appliquerais toujours l'unicité de l'utilisation à l'aide d'une contrainte de vérification, mais il faudrait que les tables croisées soient différentes pour chaque saveur de SQL (par exemple, SQL Server requiert une fonction UDF pour traverser les tables dans une contrainte de vérification). Je pense toujours que vous pouvez nuke la table en T. En ce qui concerne le côté ORM des choses, je ne sais pas du tout, donc je ne peux pas en parler. Mais si vous appliquez la relation au niveau de la base de données, la manière dont vous l'implémentez via le code ne devrait pas poser de problème, car la base de données est responsable de l'intégrité des données. Cependant, il peut présenter un problème avec le recouvrement de l'erreur spécifique qui survient si, à l'exécution, la règle de la contrainte de vérification est violée.

Je devrais également mentionner que s'il y a une grande (ou même assez grande) quantité de données entrant dans les tables en question, l'approche que je recommande pourrait ne pas être la meilleure, car votre contrainte de vérification devra vérifier 20 tables pour appliquer la règle.

+0

Il n'y a pas que 3 tables concernant T, plus proche de 20 (dans mon vrai problème). Vous avez raison, la seule raison pour laquelle j'ai T il y a une façon simplifiée (?) De dire A, B, C ... a beaucoup de S. Si je nuke T, alors S devient le tableau avec tous les fk_a, fk_b , etc colonnes. Comme mentionné dans d'autres commentaires, l'unicité de A, B, C ... n'est pas une préoccupation majeure - je suis plus préoccupé que je l'ai modélisé d'une manière que Yii (ou un autre ORM) peut fonctionner correctement. – jan

+0

Vous avez raison. Je pense que puisque cela va grandir raisonnablement, j'ai besoin de repenser mon design et de me débarrasser de certaines hypothèses que j'ai faites quand j'ai commencé. Merci de votre aide! – jan

0

Je dois faire face à une situation similaire il y a quelques semaines (pas mon propre DB, je préfère combiner toutes les tables en une, sauf dans des situations très spécifiques).
La solution que j'était mis en œuvre: Dans le fichier modèle « T » Je l'ai fait quelque chose comme cela à des relations() fonction:

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

J'espère que cela vous aide.
Cordialement.

Questions connexes