it-swarm.com.de

Können zwei Tabellen in SQL aufeinander bezogen werden?

In diesem System speichern wir Produkte, Bilder von Produkten (für ein Produkt können viele sein) und ein Standardbild für ein Produkt. Die Datenbank:

CREATE TABLE  `products` (
  `ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  `DESCRIPTION` text NOT NULL,
  `ENABLED` tinyint(1) NOT NULL DEFAULT '1',
  `DATEADDED` datetime NOT NULL,
  `DEFAULT_PICTURE_ID` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `Index_2` (`DATEADDED`),
  KEY `FK_products_1` (`DEFAULT_PICTURE_ID`),
  CONSTRAINT `FK_products_1` FOREIGN KEY (`DEFAULT_PICTURE_ID`) REFERENCES `products_pictures` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;


CREATE TABLE  `products_pictures` (
  `ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `IMG_PATH` varchar(255) NOT NULL,
  `PRODUCT_ID` int(10) unsigned NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `FK_products_pictures_1` (`PRODUCT_ID`),
  CONSTRAINT `FK_products_pictures_1` FOREIGN KEY (`PRODUCT_ID`) REFERENCES `products` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

wie Sie sehen, products_pictures.PRODUCT_ID -> products.ID und products.DEFAULT_PICTURE_ID -> products_pictures.ID, also eine Zyklusreferenz. Ist es o.k?

30
John Smith

Nein, es ist nicht in Ordnung. Kreisbezüge zwischen Tabellen sind unordentlich. Siehe diesen (Jahrzehnt alten) Artikel: SQL By Design: The Circular Reference

Einige DBMS können diese Probleme mit besonderer Sorgfalt behandeln, aber MySQL wird Probleme haben.


Die erste Wahl besteht darin, als eine Ihrer Konstruktionen eine der beiden FKs auf Null zu setzen. Dadurch können Sie das Henne-und-Ei-Problem lösen (in welche Tabelle sollte ich zuerst einfügen?).

Es gibt jedoch ein Problem mit Ihrem Code. Dadurch kann ein Produkt ein Standardbild haben, bei dem dieses Bild auf ein anderes Produkt verweist.

Um einen solchen Fehler auszuschließen, sollte Ihre FK-Einschränkung Folgendes sein:

CONSTRAINT FK_products_1 
  FOREIGN KEY (id, default_picture_id) 
  REFERENCES products_pictures (product_id, id)
  ON DELETE RESTRICT                            --- the SET NULL options would 
  ON UPDATE RESTRICT                            --- lead to other issues

Dies erfordert eine UNIQUE-Einschränkung/einen Index in der Tabelle products_pictures im (product_id, id), damit der obige FK definiert ist und ordnungsgemäß funktioniert.


Ein anderer Ansatz besteht darin, die Default_Picture_ID-Spalte aus der product-Tabelle zu entfernen und eine IsDefault BIT-Spalte in die picture-Tabelle einzufügen. Das Problem bei dieser Lösung ist, wie nur einem Bild pro Produkt dieses Bit und alle anderen das Bild deaktiviert werden können. In SQL-Server (und ich denke in Postgres) kann dies mit einem Teilindex erfolgen:

CREATE UNIQUE INDEX is_DefaultPicture 
  ON products_pictures (Product_ID)
  WHERE IsDefault = 1 ;

MySQL hat jedoch keine solche Funktion.


Ein dritter Ansatz, bei dem Sie sogar beide FK-Spalten als NOT NULL definieren können, besteht darin, verschiebbare Einschränkungen zu verwenden. Das funktioniert in PostgreSQL und ich denke in Oracle. Überprüfen Sie diese Frage und die Antwort von @Erwin: Komplexe Fremdschlüsseleinschränkung in SQLAlchemy (die Alle Schlüsselspalten NOT NULL Part).

Einschränkungen in MySQL können nicht verschoben werden.


Ein vierter Ansatz (den ich am saubersten finde) ist, die Default_Picture_ID-Spalte zu entfernen und eine weitere Tabelle hinzuzufügen. Kein kreisförmiger Pfad in den FK-Einschränkungen und alle FK-Spalten werden mit dieser Lösung NOT NULL sein:

product_default_picture
----------------------
product_id          NOT NULL
default_picture_id  NOT NULL
PRIMARY KEY (product_id)
FOREIGN KEY (product_id, default_picture_id)
  REFERENCES products_pictures (product_id, id)

Dies erfordert auch eine UNIQUE-Einschränkung/einen Index in der Tabelle products_pictures auf (product_id, id) wie in Lösung 1.


Zusammenfassend haben Sie mit MySQL zwei Möglichkeiten:

  • option 1 (eine nullfähige FK-Spalte) mit der obigen Korrektur, um die Integrität korrekt durchzusetzen

  • option 4 (keine nullfähigen FK-Spalten)

40
ypercubeᵀᴹ

dies ist nur ein Vorschlag, aber wenn möglich, erstellen Sie eine Verknüpfungstabelle zwischen dieser Tabelle, die für das Tracking hilfreich sein kann

product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
4
Pranay Rana

Das einzige Problem, dem Sie begegnen werden, ist, wenn Sie Einsätze machen. Welches fügst du zuerst ein?

Damit müssen Sie Folgendes tun:

  • Produkt mit null Standardbild einfügen
  • Fügen Sie Bilder mit der neu erstellten Produkt-ID ein
  • Aktualisieren Sie das Produkt, um das Standardbild auf das gerade eingefügte Bild festzulegen.

Löschen macht wieder keinen Spaß.

In der anderen Tabelle können Sie dieses Feld einfach ohne die Fremdschlüsseleinschränkung halten. Dies ist in einigen Fällen hilfreich, in denen Sie mit der kleineren Tabelle arbeiten möchten, sich jedoch mit dem Ergebnis des Vorgangs mit der größeren Tabelle verbinden möchten.

Wenn Sie beispielsweise eine product_location-Tabelle hinzufügen, die Informationen zu Land, Bezirk, Stadt, Adresse sowie Längen- und Breitengrad enthält. Möglicherweise möchten Sie das Produkt in einem Kreis auf der Karte anzeigen.

1
Jackie

John, was Sie tun, ist nicht schlecht, aber die Verwendung von PK-FK hilft tatsächlich dabei, Ihre Daten zu normalisieren, indem Sie redundante, sich wiederholende Daten entfernen. Das hat einige fantastische Vorteile

  • Verbesserte Datenintegrität durch den Wegfall doppelter Speicherorte für dieselben Daten
  • Reduzierte Sperrenkonflikte und verbesserte Parallelität für mehrere Benutzer
  • Kleinere Dateien
0
Killrawr