44

このシステムでは、製品、製品の画像 (製品には多くの画像が存在する可能性があります)、および製品のデフォルトの画像を保存します。データベース:

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;

ご覧のとおり、products_pictures.PRODUCT_ID -> products.IDそしてproducts.DEFAULT_PICTURE_ID -> products_pictures.ID、サイクル参照です。大丈夫ですか?

4

6 に答える 6

60

いいえ、大丈夫ではありません。テーブル間の循環参照は厄介です。この (10 年前の) 記事を参照してください: SQL By Design: The Circular Reference

一部の DBMS はこれらを処理でき、特別な注意が必要ですが、MySQL には問題があります。


オプション1

あなたの設計として、2 つの FK の 1 つを null 可能にします。これにより、ニワトリが先か卵が先かという問題 (最初にどのテーブルに挿入すればよいか) を解決できます。

ただし、コードに問題があります。製品が別の製品を参照するデフォルトの写真を持つことができます!

このようなエラーを許可しないようにするには、FK 制約を次のようにする必要があります。

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

これには、上記の FK を定義して適切に機能させるために、テーブルのUNIQUE制約/インデックスが必要です。products_pictures(product_id, id)


オプション 2

もう 1 つの方法は、テーブルからDefault_Picture_ID列を削除し、テーブルに列productを追加することです。この解決策の問題点は、製品ごとに 1 つの写真だけにそのビットをオンにし、他のすべての写真にはオフにする方法です。SQL-Server (および私は Postgres と思います) では、これは部分インデックスで実行できます。IsDefault BITpicture

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

しかし、MySQL にはそのような機能はありません。


オプション 3

NOT NULLこのアプローチでは、遅延可能な制約を使用するために、両方の FK 列をそのまま定義することもできます。これは PostgreSQL で機能し、私は Oracle で考えています。この質問と @Erwin による回答を確認してください: SQLAlchemy の複雑な外部キー制約(すべてのキー列は NULL部分ではありません)。

MySQL の制約は延期できません。


オプション 4

アプローチ(私が最もきれいだと思う)は、Default_Picture_ID列を削除して別のテーブルを追加することです。FK 制約に循環パスはなく、すべての FK 列はNOT NULLこのソリューションを使用します。

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)

これには、ソリューション 1 のように、UNIQUEテーブルの制約/インデックスも必要です。products_pictures(product_id, id)


要約すると、MySQL には 2 つのオプションがあります。

  • 整合性を正しく適用するための上記の修正を含むオプション 1 (null 許容 FK 列)

  • オプション 4 (NULL 可能な FK 列なし)

于 2012-05-05T01:45:20.687 に答える
3

これは単なる提案ですが、可能であれば、このテーブル間に 1 つの結合テーブルを作成すると追跡に役立つ場合があります

product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
于 2012-05-04T10:01:25.323 に答える
3

遭遇する唯一の問題は、挿入を行うときです。どちらを先に挿入しますか?

これにより、次のようなことを行う必要があります。

  • デフォルト画像が null の製品を挿入
  • 新しく作成された製品 ID を使用して画像を挿入します
  • 製品を更新して、デフォルトの画像を挿入したばかりの画像に設定します。

繰り返しますが、削除は楽しくありません。

于 2012-05-04T10:04:11.547 に答える
0

ジョン、あなたの行動は何も悪いことではありませんが、PK-FK を使用すると、冗長な繰り返しデータを削除してデータを正規化するのに役立ちます。いくつかの素晴らしい利点があります

  • 同じデータの重複した保管場所を排除することにより、データの整合性が向上します
  • ロックの競合が減少し、複数ユーザーの同時実行が改善されました
  • より小さいファイル
于 2012-05-05T02:00:54.153 に答える
-2

それは循環参照ではありません。つまり、pk-fk です。

于 2012-05-04T10:00:02.350 に答える