質問
次のようなデータベーススキーマの設計を考えています:
Person (
PersonID int primary key,
PrimaryAddressID int not null,
...
)
Address (
AddressID int primary key,
PersonID int not null,
...
)
Person.PrimaryAddressIDおよびAddress.PersonIDは、対応するテーブルへの外部キーです。
明らかな問題は、どちらのテーブルにも何も挿入できないことです。プライマリアドレスを持つすべての人を強制する作業スキーマを設計する方法はありますか?
解決
<!> quot;これは不可能だと思います。個人のIDがわかるまで住所レコードを作成することはできず、PrimaryAddressIdフィールドのAddressIdがわかるまで個人レコードを挿入することはできません。<!> quot;
一見すると、その主張は魅力的だと思われます。ただし、それはかなり前向きです。
これは非常に一般的な種類の問題であり、SQL DBMSベンダーはすでに数十年にわたって攻撃を試みています。
重要なことは、すべての制約チェックが<!> quot; deferred <!> quot;でなければならないことです。両方の挿入が完了するまで。それはさまざまな形で実現できます。データベーストランザクションは、<!> quot; SET deferred constraint checking ON <!> quot;のようなことをする可能性を提供し、これで完了です(この特定の例では、おそらく、 2つのFK制約のみを定義できるようにするために、設計が非常に困難になります。なぜなら、それらの1つは単にSQLの意味で「真の」FKではないからです!)
ここで説明するトリガーベースのソリューションは、本質的に同じ効果を達成しますが、アプリケーションによって強制される整合性に存在するすべてのメンテナンスの問題にさらされます。
Chris Date <!> amp;ヒュー・ダーウェンは、問題の真の解決策であるimoについて説明しています。つまり、本質的に、いくつかの個別の更新ステートメントを作成し、DBMSにそれが単一のステートメントであるかのように動作させる可能性です。この概念の実装は存在しますが、SQLに対応するものは見つかりません。
他のヒント
アドレステーブルでプライマリアドレスをマークし、1人あたり1つのレコードのみを保持できるトリガーを使用します(ただし、1つのレコードが保持する必要があります)。プライマリアドレスを変更すると、古いプライマリアドレスと新しいプライマリアドレスが更新されます。プライマリアドレスを削除し、他のアドレスが存在する場合、それらの1つ(一連のルールに基づいて)をプライマリアドレスに昇格します。アドレスが挿入され、最初に挿入されたアドレスである場合、そのアドレスが自動的にプライマリアドレスとしてマークされます。
これは、多対多の関係の完璧な例です。これを解決するには、中間のPERSON_ADDRESSテーブルが必要です。つまり、
PERSON table
person_id (PK)
ADDRESS table
address_id (PK)
PERSON_ADDRESS
person_id (FK) <= PERSON
address_id (FK) <= ADDRESS
is_primary (BOOLEAN - Y/N)
この方法では、複数の住所を1人に割り当てることができ、ADDRESSレコードを複数の人(家族、同じ会社の従業員など)で再利用することもできます。 PERSON_ADDRESSテーブルのis_primaryフィールドを使用すると、そのperson_addreesの組み合わせが個人のプライマリアドレスかどうかを識別できます。
2番目のFK(AddressからPersonへのPersonId)は制限が厳しすぎます。 1つの住所に1人の人物しか入らないことを提案していますか?
設計上、アドレスは1人にしか適用できないようです。そのため、PersonIDをアドレステーブルのキーとして使用し、AddressIDキーフィールドをドロップします。
おそらく十字架につけられるかどうかはわかっていますが、ここに行きます...
<!> quot;特定の非常に独自の非標準の<!> quot;については、このようにしました。ビジネスニーズ(=(私が話すときでも、SQL DDLのように聞こえ始めています。)
例を次に示します。
CREATE TABLE IF NOT EXISTS PERSON(
ID INT,
CONSTRAINT PRIMARY KEY (ID),
ADDRESS_ID INT NOT NULL DEFAULT 1,
DESCRIPTION VARCHAR(255),
CONSTRAINT PERSON_UQ UNIQUE KEY (ADDRESS_ID, ...));
INSERT INTO PERSON(ID, DESCRIPTION)
VALUES (1, 'GOVERNMENT');
CREATE TABLE IF NOT EXISTS ADDRESS(
ID INT,
CONSTRAINT PRIMARY KEY (ID),
PERSON_ID INT NOT NULL DEFAULT 1,
DESCRIPTION VARCHAR(255),
CONSTRAINT ADDRESS_UQ UNIQUE KEY (PERSON_ID, ...),
CONSTRAINT ADDRESS_PERSON_FK FOREIGN KEY (PERSON_ID) REFERENCES PERSON(ID));
INSERT INTO ADDRESS(ID, DESCRIPTION)
VALUES (1, 'ABANDONED HOUSE AT THIS ADDRESS');
ALTER TABLE PERSON ADD CONSTRAINT PERSON_ADDRESS_FK FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS(ID);
<!> lt; ...人生は続く...あなたがその人に提供し、アドレス指定するかどうか、その逆も同様です<!> gt;
1つのテーブルを定義し、次に他のテーブルが最初のテーブルを参照してから、2番目のテーブルへの参照を反映するように最初のテーブルを変更しました(最初のテーブルの作成時には存在しませんでした)。特定のデータベース向けではありません。必要な場合は試してみて、機能する場合はそれを使用します。そうでない場合は、デザインにその必要性を持たないようにします(常にそれを制御することはできません、時々デザインがそのまま渡されます) 。人のいない住所がある場合は、<!> quot; government <!> quot;に属します。人。 <!> quot;ホームレスの人<!> quot;その後、<!> quot;廃屋<!> quot;を取得します。住所。どの家にユーザーがいないかを判断するプロセスを実行します