データベースで循環参照が許容されるのはいつですか?
理論的かつ実用的で、どんな助けでも大歓迎です。
都市と州を考えてみましょう。各都市は州内に存在します。各州には州都があります。
CREATE TABLE city (
city VARCHAR(32),
state VARCHAR(32) NOT NULL,
PRIMARY KEY (city),
FOREIGN KEY (state) REFERENCES state (state)
);
CREATE TABLE state (
state VARCHAR(32),
capital_city VARCHAR(32),
PRIMARY KEY (state),
FOREIGN KEY (capital_city) REFERENCES city (city)
);
最初の問題 - 外部キーは (まだ) 存在しないテーブルの列を参照できないため、これらのテーブルを作成することはできません。解決策は、外部キーなしでそれらを作成し、後で外部キーを追加することです。
2 番目の問題 - 各挿入には他のテーブルの既存の行が必要になるため、どちらのテーブルにも行を挿入できません。解決策は、外部キー列の 1 つを NULL に設定し、そのデータを 2 段階で挿入することです。例えば
--Create state record
INSERT INTO state (state, capital_city) VALUES ('Florida', NULL);
--Create various city records
INSERT INTO city (city, state) VALUES ('Miami', 'Florida');
INSERT INTO city (city, state) VALUES ('Tallahassee', 'Florida');
INSERT INTO city (city, state) VALUES ('Orlando', 'Florida');
--Set one of the cities as the capital
UPDATE state SET capital_city = 'Tallahassee' WHERE state = 'Florida';
他のレコードを指すレコードは、データベースで役立ちます。これらのレコードがサイクルを形成する場合があります。これはまだ役立つかもしれません。実際の唯一の本当の煩わしさは、制約に違反しないようにすることです。
たとえば、ユーザーとトランザクションテーブルがある場合、ユーザーは最後のトランザクションへのポインタを持っている可能性があります。last_transaction_id
最初にトランザクションを挿入してから、を正しい値に更新する必要があります。user.last_transaction_id
これらのレコードは両方とも存在しますが、をポイントしtransaction.id
、をtransaction.user_id
ポイントしているため、それらを消去することはできませんuser.id
。これは、トランザクションのないユーザーがnullを持っていることを意味しますlast_transaction_id
。また、トランザクションを削除する前に、そのフィールドをnullにする必要があることも意味します。
これらの外部キー制約を管理するのは面倒ですが、それは確かに可能です。後でデータベースに制約を追加して新しい循環依存関係を導入すると、問題が発生する可能性があります。この状況では注意する必要があります。ただし、サイクル内のレコードの1つにnull許容の外部キーフィールドがある限り、サイクルが中断され、レコードが削除される可能性があります。レコードを正しい順序で挿入する限り、通常、更新は問題になりません。
Oracle階層クエリ構文に最近追加されたものの1つであるNOCYCLE
キーワードは、データ内の循環参照を処理するために、この目的のために明示的に作成されました。私はそれについて何も悪いことは見ていません、そして以前にこの種のモデルを扱わなければなりませんでした。特に、延期可能な制約をサポートするOracleでは、それほど難しくありません。
技術的には可能ですが、鶏が先か卵が先かという問題が発生するため、レコードを削除するときにさまざまな問題が発生する可能性があります。これらの問題は、多くの場合、FKを手動で削除したり、問題のあるアイテムを削除して解決したりするなど、抜本的なアクションを実行します。
あなたが次のような関係を持っている場合:
create table foo_master (
foo_master_id int not null primary key
,current_foo_id int
)
create table foo_detail (
foo_detail_id int not null primary key
foo_master_id int not null
)
alter table foo_master
add constraint fk_foo_current_detail
foreign key (current_foo_id)
references foo_detail
alter table foo_detail
add constraint fk_foo_master
foreign key (foo_master_id)
references foo_master
次に、レコードを削除すると、循環依存関係が原因で、このような鶏が先か問題が発生する可能性があります。
このためのより良いスキーマは次のようになります。
create table foo_master (
foo_master_id int not null primary key
)
create table foo_detail (
foo_detail_id int not null primary key
foo_master_id int not null
is_current char (1)
)
alter table foo_detail
add constraint fk_foo_master
foreign key (foo_master_id)
references foo_master
これは、関係が非循環的であり、「現在の」foo_detailレコードを引き続き識別できることを意味します。
パフォーマンス上の理由から循環参照が行われるのを見てきました。見た目は悪いですが、パフォーマンスは無視できるかもしれません。
例: 一部の掲示板 (phpBB がこれを行っていると思います) には、カテゴリ テーブルに、スレッドの最後の投稿へのショートカットである lastpostid があります。
これにより、最後の投稿にカテゴリ テーブルへの FK があり、カテゴリ テーブルには最後の投稿に戻る FK がある円が作成されます。
私が言ったように、私はそれがあまり好きではありませんが、私はそれが行われたのを見てきました.
循環参照は疫病のように避けるべきです。双方向のリレーションシップ、または自分自身とのリレーションシップ (テーブルの場合) を設定することは可能ですが、循環依存関係は問題を引き起こしているだけです。
必要な1対1の関係に出くわすことはめったになく、循環関係を強いる
このような関係の外部キー フィールドは null 許容でなければならないことに注意してください。そうしないと、テーブルから行を削除できません。
書き込み専用データベースを使用している場合は問題ないと思います。CRUD の RUD 部分を使用することを計画している場合、それらを処理する際に (通常は回避可能な) 複雑な問題に遭遇する可能性があります。