2008-09-22 4 views
6

Wenn ich eine Tabelle in MySQL habe, die eine Basisklasse darstellt, und ich habe eine Reihe von Tabellen, die die Felder in den abgeleiteten Klassen repräsentieren, die jeweils auf die Basistabelle mit einem Fremdschlüssel verweisen, gibt es einen Weg dazu MySQL dazu bringen, die Eins-zu-Eins-Beziehung zwischen der abgeleiteten Tabelle und der Basistabelle zu erzwingen, oder muss das im Code geschehen?MySQL Fremdschlüssel - wie One-to-One über mehrere Tabellen zu erzwingen?

Verwenden Sie das folgende schnell 'n' Dirty-Schema als Beispiel, gibt es eine Möglichkeit, MySQL dazu zu bringen, sicherzustellen, dass Zeilen in product_cd und product_dvd nicht dieselbe product_id teilen können? Gibt es eine bessere Möglichkeit, das Schema so zu entwerfen, dass die Datenbank diese Beziehung erzwingen kann, oder ist das einfach nicht möglich?

CREATE TABLE IF NOT EXISTS `product` (
    `product_id` int(10) unsigned NOT NULL auto_increment, 
    `product_name` varchar(50) NOT NULL, 
    `description` text NOT NULL, 
    PRIMARY KEY (`product_id`) 
) ENGINE = InnoDB; 

CREATE TABLE `product_cd` (
    `product_cd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
    `product_id` INT UNSIGNED NOT NULL , 
    `artist_name` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`product_cd_id`) , 
    INDEX (`product_id`) 
) ENGINE = InnoDB; 

ALTER TABLE `product_cd` ADD FOREIGN KEY (`product_id`) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ; 

CREATE TABLE `product_dvd` (
    `product_dvd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
    `product_id` INT UNSIGNED NOT NULL , 
    `director` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`product_dvd_id`) , 
    INDEX (`product_id`) 
) ENGINE = InnoDB; 

ALTER TABLE `product_dvd` ADD FOREIGN KEY (`product_id`) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ; 

@Skliwz, können Sie bitte genauer darüber, wie Trigger verwendet werden kann diese Einschränkung mit dem Schema zur Verfügung gestellt zu erzwingen?

@boes, das hört sich gut an. Wie funktioniert es in Situationen, in denen Sie ein Kind eines Kindes haben? Zum Beispiel, wenn wir product_movie hinzugefügt und product_dvd ein Kind von product_movie gemacht haben? Wäre es ein Alptraum der Wartbarkeit, wenn die Checkbeschränkung für product_dvd auch alle untergeordneten Typen berücksichtigt?

+5

Verwenden Sie Kommentare zu Antworten, anstatt Ihre Frage zu bearbeiten. – epochwolf

Antwort

3

Um sicherzustellen, dass ein Produkt oder eine CD oder eine DVD ist, würde ich eine Typspalte hinzufügen und sie Teil des Primärschlüssels machen. In der abgeleiteten Spalte fügen Sie eine Prüfbedingung für den Typ hinzu. Im Beispiel habe ich cd auf 1 gesetzt und Sie könnten DVD = 2 und so weiter für jede abgeleitete Tabelle machen.

CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(10) unsigned NOT NULL auto_increment, 
'product_type' int not null, 
`product_name` varchar(50) NOT NULL, 
`description` text NOT NULL, 
PRIMARY KEY (`product_id`, 'product_type') 
) ENGINE = InnoDB; 

CREATE TABLE `product_cd` (
`product_id` INT UNSIGNED NOT NULL , 
'product_type' int not null default(1) check ('product_type' = 1) 
`artist_name` VARCHAR(50) NOT NULL , 
PRIMARY KEY (`product_id`, 'product_type') , 
) ENGINE = InnoDB; 

ALTER TABLE `product_cd` ADD FOREIGN KEY (`product_id`, 'product_type') 
REFERENCES `product` (`product_id`, 'product_type') 
ON DELETE RESTRICT ON UPDATE RESTRICT ; 
+2

-1. MySQL ignoriert Prüfbedingungen. –

1

Wenn Sie MySQL 5.x verwenden, können Sie Trigger für diese Art von Einschränkungen verwenden.

Eine andere (suboptimale) Option wäre die Verwendung einer Spalte "type" in der übergeordneten Tabelle, um die Duplizierung stillschweigend zu ignorieren und die richtige "Erweiterungstabelle" auswählen zu können.

+1

Wie könnten Sie einen Trigger für diese Art von Constraint verwenden? Können Sie bitte ein Beispiel geben? – Shabbyrobe

3

Sie könnten einfach einen Fremdschlüssel von einem Primärschlüssel zum anderen Primärschlüssel hinzufügen. Da PKs eindeutig sein müssen, erhalten Sie automatisch eine Eins-zu-Eins-Beziehung.

+0

Mit InnoDB funktioniert das bei mir nicht. selbst nach dem Reverse-Engineering des Skripts bleibt die Beziehung eins-zu-viele. Die einzige effektive Möglichkeit zum Beibehalten einer Eins-zu-Eins-Beziehung besteht darin, die Fremdschlüsselspalte auf UNIQUE zu setzen. Auf diese Weise sind wir sicher, dass nur ein Datensatz dafür erstellt wird. Und wenn Sie versuchen, diese Einschränkung zu verletzen, erhalten Sie den folgenden Fehler (MySQL 5.1): '1 Fehler (s) Speichern von Änderungen in Tabelle' testdb'.table4': INSERT INTO 'testdb'.table4' ('idtable4',' label', 'table3_idtable3') WERTE (0, 'soso', 0) 1062: Doppelter Eintrag '0' für Schlüssel 'table3_idtable3_UNIQUE' Rollback complete ' – Hanynowsky

4

Wenn Sie losgeworden Produkt-dvd-ID und Produkt-CD-ID und verwendet, um die Produkt-ID als Primärschlüssel für alle drei Tabellen, Sie könnten zumindest sicherstellen, dass keine zwei DVD oder keine zwei CD verwenden das gleiche Produkt-ID. Außerdem gäbe es weniger IDs, um den Überblick zu behalten.

Und Sie brauchen vielleicht eine Art von Typ Spalte in der Produkttabelle.

8

Die Erzwingung einer 1: 0-1- oder 1: 1-Beziehung kann durch Definieren einer eindeutigen Integritätsbedingung für die Spalten des Fremdschlüssels erreicht werden, sodass nur eine Kombination vorhanden sein kann. Normalerweise wäre dies der Primärschlüssel der Kindtabelle.

Wenn sich der FK auf einem primären oder eindeutigen Schlüssel der referenzierten Tabellen befindet, wird er auf Werte beschränkt, die im übergeordneten Element vorhanden sind, und die eindeutige Integritätsbedingung für die Spalte oder Spalten beschränkt diese auf die Eindeutigkeit. Dies bedeutet, dass die untergeordnete Tabelle nur Werte enthalten kann, die dem übergeordneten Element in den eingeschränkten Spalten entsprechen, und jede Zeile muss einen eindeutigen Wert haben. Dadurch wird sichergestellt, dass die untergeordnete Tabelle höchstens eine Zeile hat, die dem übergeordneten Datensatz entspricht.

+4

Minus 1, weil er nicht antwortet Frage, bietet kein Beispiel und bekommt immer noch viele Stimmen. Ein Teil der Frage lautet: "Gibt es eine Möglichkeit, sicherzustellen, dass Zeilen in product_cd und product_dvd nicht die gleiche product_id teilen können? Du antwortest das nicht. Wer gibt dir diese Stimmen? – boes

1

Die einzige effektive Art und Weise eine Eins-zu-Eins-Beziehung zu halten, ist es, die Fremdschlüssel-Spalte UNIQUE einzustellen; Auf diese Weise sind wir sicher, dass nur ein Datensatz dafür erstellt wird. Und wenn Sie versuchen, diese Einschränkung zu verletzen, erhalten Sie den folgenden Fehler (MySQL 5.1):

`1 error(s) saving changes to table testdb`.table4: INSERT INTO testdb.table4 (idtable4, label, table3_idtable3) VALUES (0, 'soso', 0) 1062: Duplicate entry '0' for key 'table3_idtable3_UNIQUE' Rollback complete