2011-08-03 15 views
4

私の同僚の1人が、次のようなスキーマを作成しました。これは、この質問に対処するために必要な部分だけを含む単純化されたスキーマです。次のように複数のテーブルのIDを単一の列で使用する

システムルールは以下のとおりです。

  1. 部門は、多くの部門に0を持つことができます。
  2. 部門は1つの部門にのみ属している必要があります。
  3. 記事は、部門またはその部門の部門に割り当てることができます。

スキーマである:

Department 
---------- 
DepartmentID (PK) int NOT NULL 
DepartmentName varchar(50) NOT NULL 

Division 
-------- 
DivisionID (PK) int NOT NULL 
DepartmentID (FK) int NOT NULL 
DivisonName varchar(50) NOT NULL 

Article 
------- 
ArticleID (PK) int NOT NULL 
UniqueID int NOT NULL 
ArticleName varchar(50) NOT NULL 

彼はすべてDepartmentIDsは1と100の間であろうと、(より良い用語の欠如のために)仮想ルールを使用してスキーマを定義し、すべてのDivisionIDsの間であろう彼は、Articleテーブルを照会するときにUniqueIDがDepartmentテーブルかDivisionテーブルのどちらであるのかを知ることができます。

私はこれが悪いデザインだと思うと、次の代替スキーマ提案:上記で概説したビジネスルールを尊重しながら、私は、これは適切に正規化されたスキーマで、適切な関係とデータの整合性を強制すると信じて

Department 
---------- 
DepartmentID (PK) int NOT NULL 
ParentDepartmentID (FK) int NULL /* Self-referencing foreign key. Divisions have parent departments. */ 
DepartmentName varchar(50) NOT NULL 

Article 
------- 
ArticleID (PK) int NOT NULL 
DepartmentID (FK) int NOT NULL 
ArticleName varchar(50) NOT NULL 

を。

私の具体的な質問はこれです:私は2つのドメインからの値を含むように1列を使用して知っている

貧弱な設計であり、私は記事の表の外部キーの利点を主張することができます。しかし、誰かが私の位置をバックアップするために使用できる特定のデータベース設計記事/論文への参照を提供することができます。私が何か具体的なものを指すことができれば、はるかに簡単になります。

+0

以下に示す詳細、議論、および代替ソリューションを高く評価します。私は私の問題についての洞察を感じた答えをアップに投票しました。しかし、私は彼の答えが特定の質問に直接対処していると感じたので、私はDamirの答えを授与しました。 – NYSystemsAnalyst

答えて

2

1NF

すべての行と列の交点が 該当ドメイン(何もない)から1つの値が含まれています。

http://en.wikipedia.org/wiki/First_normal_form#1NF_tables_as_representations_of_relations

あなたの問題を解決する最も簡単な方法は、単純に「全体の部門」を意味し、各部門のための「デフォルト」部門を導入することです。その後、すべての記事を部門にリンクするだけです。 は多分これ(DepartmentDivisionNo = 0が全体の部門を意味する)のようなもの:

enter image description here

+0

リンクありがとうございました。私はあなたの提案したスキーマを別の方法として見ています。しかし、なぜDepartmentDivisionNoの代わりにDivisionIDを持っていて、記事が部門に関係するのはなぜですか?その後、記事から部門までFKが1つしかありません。私が部に戻らなければならない場合、もう一度参加することができます。 – NYSystemsAnalyst

+0

@NYSystemsAnalyst;はい、それはうまくいくでしょう。同じ部署内で部門を繰り返さないようにするには何かが必要なので、「部門」の余分なインデックスが必要になります。これにより、 'Article'を' Department 'に直接参加させることができます。とにかく、あなたの好み。これも参照してくださいhttp://stackoverflow.com/questions/4520289/multiple-column-foreign-key-contraints/4522171#4522171 –

+1

部門が部門ではなく、部門は部門ではない場合は、作成するのがはるかに貧しい設計ですデータベースには、各部署には「部門全体」を表すので実際には存在しない部門が存在するとふりまとう。 –

4

同僚はポリモーフィック団体と呼ばれるデザインを実現しました。つまり、「外部キー」は、2つの異なる親表のうちの1つを指します。ほとんどの人は別の列parent_typeなどを追加して、特定の行がどの親テーブルに参照されているかを知ることができます。あなたの同僚の場合、彼は代わりにIDの範囲を細分しました。これはデータベースレベルでは実行できないため、脆弱な設計です。部門番号> 100を挿入すると、記事が部門または部門に適用されているかどうかを知ることができません。

Single Table Inheritanceのようなデザインを開発しましたが、複数の関連タイプを1つのテーブルに格納するので、主キーは一意のままであり、関連するタイプのインスタンスはすべて参照できます。

別の方法があります。

オブジェクト指向設計を考えてください。 2つの異なるクラスが記事を持つことを許可したい場合は、2つのクラスの共通のスーパークラスまたは共通のインターフェースを作成することができます。あなたは、SQLで同じことを行うことができます

ArticleProducer 
--------------- 
ProducerID (PK) int NOT NULL 

Department 
---------- 
DepartmentID (PK) int NOT NULL, (FK)->ArticleProducer 
DepartmentName varchar(50) NOT NULL 

Division 
-------- 
DivisionID (PK) int NOT NULL, (FK)->ArticleProducer 
DepartmentID (FK) int NOT NULL 
DivisonName varchar(50) NOT NULL 

Article 
------- 
ArticleID (PK) int NOT NULL, (FK)->ArticleProducer 
UniqueID int NOT NULL 
ArticleName varchar(50) NOT NULL 

ので記事は、単一のArticleProducerによって生成されなければなりません。各部門または部門はArticleProducerです。

は私のプレゼンテーションPractical Object-Oriented Models in SQL、または私の本、SQL Antipatterns: Avoiding the Pitfalls of Database Programmingを参照してください、ポリモーフィック関連付けの詳細についてもWhy can you not have a foreign key in a polymorphic association?

を参照してください。すべてのサブタイプのテーブルからこれ以上より行は少しトリッキーであることを強制しようとして

そうだね、:


アーウィンSmoutから再コメント。残念ながら、MySQLはどのストレージエンジンでもCHECK制約をサポートしていません。

CREATE TABLE ArticleProducerTypes (ProducerType TINYINT UNSIGNED PRIMARY KEY); 
INSERT INTO ArticleProducerTypes VALUES (1), (2); 

CREATE TABLE ArticleProducer (
    ProducerID INT UNSIGNED NOT NULL PRIMARY KEY, 
    ProducerType TINYINT UNSIGNED NOT NULL, 
    UNIQUE KEY (ProducerID,ProducerType), 
    FOREIGN KEY (ProducerType) 
    REFERENCES ArticleProducerTypes(ProducerType) 
) ENGINE=InnoDB; 

CREATE TABLE DepartmentProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY); 
INSERT INTO DepartmentProducerType VALUES (1); 

CREATE TABLE Department (
    DepartmentID INT UNSIGNED NOT NULL PRIMARY KEY, 
    DepartmentName VARCHAR(50) NOT NULL, 
    ProducerType TINYINT UNSIGNED NOT NULL, 
    FOREIGN KEY (DepartmentID, ProducerType) 
    REFERENCES ArticleProducer(ProducerID, ProducerType), 
    FOREIGN KEY (ProducerType) 
    REFERENCES DepartmentProducerType(ProducerType) -- restricted to '1' 
) ENGINE=InnODB; 

CREATE TABLE DivisionProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY); 
INSERT INTO DivisionProducerType VALUES (2); 

CREATE TABLE Division (
    DivisionID INT UNSIGNED NOT NULL PRIMARY KEY, 
    ProducerType TINYINT UNSIGNED NOT NULL, 
    DepartmentID INT UNSIGNED NOT NULL, 
    FOREIGN KEY (DivisionID, ProducerType) 
    REFERENCES ArticleProducer(ProducerID, ProducerType), 
    FOREIGN KEY (ProducerType) 
    REFERENCES DivisionProducerType(ProducerType), -- restricted to '2' 
    FOREIGN KEY (DepartmentID) 
    REFERENCES Department(DepartmentID) 
) ENGINE=InnODB; 

CREATE TABLE Article (
    ArticleID INT UNSIGNED NOT NULL PRIMARY KEY, 
    ArticleName VARCHAR(50) NOT NULL, 
    FOREIGN KEY (ArticleID) 
    REFERENCES ArticleProducer(ProducerID) 
); 

ここで、ArticleProducerの各特定の行は、DepartmentまたはDivisionのいずれかで参照できますが、両方では参照できません。

新しいプロデューサ・タイプを追加する場合は、ArticleProducerTypes参照表に1行を追加し、新しいタイプの新しい表のペアを作成します。たとえば、次のように

INSERT INTO ArticleProducerTypes VALUES (3); 

CREATE TABLE PartnerProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY); 
INSERT INTO PartnerProducerType VALUES (3); 

CREATE TABLE Partner (
    PartnerID INT UNSIGNED NOT NULL PRIMARY KEY, 
    ProducerType TINYINT UNSIGNED NOT NULL, 
    FOREIGN KEY (PartnerID, ProducerType) 
    REFERENCES ArticleProducer(ProducerID, ProducerType), 
    FOREIGN KEY (ProducerType) 
    REFERENCES PartnerProducerType(ProducerType) -- restricted to '3' 
) ENGINE=InnODB; 

しかし、我々はまだどちらもArticleProducerでその特定の行への参照が含まれている可能性があります。つまり、従属テーブルの1つに行を強制的に作成するという制約を加えることはできません。私には解決策がありません。

+0

articleProducerのproducerNameを各producerテーブルよりも良い方がいいですか? – Beth

+0

@Beth、確かに、すべてのサブタイプに共通するカラムをプロデューサテーブルに入れるのは一般的です。次に、Martin FowlerのClass Table Inheritanceパターンを実装しています。 –

+0

"あなたの同僚の場合、彼は代わりにIDの範囲を細分しました。これは脆弱なデザインです。データベースレベルではそれを実行できないためです。あなたは本当にそれを言いましたか? CHECK制約は、その細分化を実施するのに十分です。宣言的に強制することができないのは、すべての記事が何か(部門または部門のいずれか)に属していなければならないというFKです。これはあなたが言うことを意図したものかもしれませんが、あなたが言ったことではありませんでした。 –

0

部門と部門は同じテーブルに格納する必要があります。このように:

これらは同じような要素です。同じように扱う必要があります。

その後、ID番号の範囲が狭くなることはありません。とにかくそのアプローチはあまりにもスケーラブルではありません。

幸運。

0

RE:彼はすべてDepartmentIDsは1と100の間であろうと、全てDivisionIDsであろうこと、(より良い用語の欠如のために)仮想ルールを使用してスキーマを定義

場合101乃至200それは彼がやりたいことだから、isDepartment yes/noのような別のフィールドを使うべきです。次に、ID、名前、isDepartmentを持つ部門と部門のテーブルが1つあり、IDフィールドはArticleテーブルのFKになります。

これは、部門と部門の重複するIDは解決しますが、部門と部門の1対多の関係は解決しません。その関係を強化するには、2つのテーブルが必要です。

ArticleとFKの関係を持つ部門テーブルと部門テーブルの両方にAuthorIDフィールドを導入することもできます。それは自動生成フィールドになる可能性があります。これは、除算表の複合キーを正規化する方法です。

1

私は実際にダミールの答えが好きです。それは質問を再考し、その新しい質問に対する正解を生成します。しかし、部門と部門の間には違いがあります。おそらく、各部門は、その部門に属する記事にアクセスする可能性があります。デフォルトまたは全部門部門に属する記事を持つことには、2つの異なるタイプの部門があることを意味します。

Department 
---------- 
DepartmentID (PK) int NOT NULL 
DepartmentName varchar(50) NOT NULL 

Division 
-------- 
DivisionID (PK) int NOT NULL 
DepartmentID (FK) int NOT NULL 
DivisonName varchar(50) NOT NULL 

Article 
------- 
ArticleID (PK) int NOT NULL 
ArticleName varchar(50) NOT NULL 

ArticleBelongsToDepartment 
-------------------------- 
ArticleID (PK) (FK) int NOT NULL 
DepartmentID (FK) int NOT NULL 

ArticleBelongsToDivision 
-------------------------- 
ArticleID (PK) (FK) int NOT NULL 
DivisionID (FK) int NOT NULL 

を今、いくつかのを強制する方法:今から、あなたは、このような代わり

select * from xxx x inner join division d where d.joinkey = x.joinkey and d.division != 0. 

として、私は私の解決策は、「関係けちるしないでください」と呼んでクエリを作るだろう育った制約?それに対処するために、各記事がビンに属し、部門と部門にビンがある「記事ビン」を作成することができます。

しかし、それは雑草に侵入しており、記事が部門、部門、ビンに依存しているか、そうでないかのいずれかを解決することができません。部門はビンに依存しているか、ビンは部門に依存しています。これらの質問の中には、おそらく夜間の整合性チェックを伴う取引やストアドプロシージャによって最もよく答えられるものがあります。

関連する問題