スタッフ、顧客、およびサプライヤーを扱うデータベース システムがあり、これらのすべてに複数の電話番号がある場合、これらの番号を適切に正規化された方法で保存するにはどうすればよいでしょうか? 少し考えてみましたが、論理的な方法が思い浮かびません。
4 に答える
ほとんどの場合 。. .
- 「スタッフ」は常に人を表します。
- 一部の顧客は人です。
- 一部の顧客は企業(組織)です。
- 「サプライヤー」は通常(常に?)組織です。
- スタッフもお客様になることができます。
- サプライヤは顧客になることもできます。
スタッフの電話番号、サプライヤの電話番号、および顧客の電話番号の個別のテーブルを持つことには、深刻な問題があります。
- スタッフはお客様になることができます。スタッフの電話番号が変わった場合、顧客の電話番号も更新する必要がありますか? どちらを更新するかをどのように知ることができますか?
- サプライヤーは顧客になることができます。サプライヤーの電話番号が変更された場合、顧客の電話番号も更新する必要がありますか? どちらを更新するかをどのように知ることができますか?
- 電話番号を格納するすべてのテーブルで、電話番号の制約を複製してエラーなく維持する必要があります。
- 顧客の電話番号が変わった場合にも同じ問題が発生します。ここで、スタッフとサプライヤーの電話番号も更新する必要があるかどうかを確認する必要があります。
- 「123-456-7890 は誰の電話番号ですか?」という質問に答えるには、「n」個の異なるテーブルを調べる必要があります。ここで、「n」は、あなたが扱うさまざまな「種類」のパーティーの数です。スタッフ、顧客、サプライヤーに加えて、「請負業者の電話」、「見込み客の電話」などを考えてください。
スーパータイプ/サブタイプ スキーマを実装する必要があります。(厳密にテストされていない PostgreSQL コード。)
create table parties (
party_id integer not null unique,
party_type char(1) check (party_type in ('I', 'O')),
party_name varchar(10) not null unique,
primary key (party_id, party_type)
);
insert into parties values (1,'I', 'Mike');
insert into parties values (2,'I', 'Sherry');
insert into parties values (3,'O', 'Vandelay');
-- For "persons", a subtype of "parties"
create table person_st (
party_id integer not null unique,
party_type char(1) not null default 'I' check (party_type = 'I'),
height_inches integer not null check (height_inches between 24 and 108),
primary key (party_id),
foreign key (party_id, party_type) references parties (party_id, party_type) on delete cascade
);
insert into person_st values (1, 'I', 72);
insert into person_st values (2, 'I', 60);
-- For "organizations", a subtype of "parties"
create table organization_st (
party_id integer not null unique,
party_type CHAR(1) not null default 'O' check (party_type = 'O'),
ein CHAR(10), -- In US, federal Employer Identification Number
primary key (party_id),
foreign key (party_id, party_type) references parties (party_id, party_type) on delete cascade
);
insert into organization_st values (3, 'O', '00-0000000');
create table phones (
party_id integer references parties (party_id) on delete cascade,
-- Whatever you prefer to distinguish one kind of phone usage from another.
-- I'll just use a simple 'phone_type' here, for work, home, emergency,
-- business, and mobile.
phone_type char(1) not null default 'w' check
(phone_type in ('w', 'h', 'e', 'b', 'm')),
-- Phone numbers in the USA are 10 chars. YMMV.
phone_number char(10) not null check (phone_number ~ '[0-9]{10}'),
primary key (party_id, phone_type)
);
insert into phones values (1, 'h', '0000000000');
insert into phones values (1, 'm', '0000000001');
insert into phones values (3, 'h', '0000000002');
-- Do what you need to do on your platform--triggers, rules, whatever--to make
-- these views updatable. Client code uses the views, not the base tables.
-- In current versions of PostgreSQL, I think you'd create some "instead
-- of" rules.
--
create view people as
select t1.party_id, t1.party_name, t2.height_inches
from parties t1
inner join person_st t2 on (t1.party_id = t2.party_id);
create view organizations as
select t1.party_id, t1.party_name, t2.ein
from parties t1
inner join organization_st t2 on (t1.party_id = t2.party_id);
create view phone_book as
select t1.party_id, t1.party_name, t2.phone_type, t2.phone_number
from parties t1
inner join phones t2 on (t1.party_id = t2.party_id);
これをさらに拡張するには、「staff」を実装するテーブルで、party スーパータイプではなく person サブタイプを参照する必要があります。組織はスタッフになることはできません。
create table staff (
party_id integer primary key references person_st (party_id) on delete cascade,
employee_number char(10) not null unique,
first_hire_date date not null default CURRENT_DATE
);
サプライヤが個人ではなく組織のみである場合、サプライヤを実装するテーブルは同様の方法で組織のサブタイプを参照します。
ほとんどの企業では、顧客は個人または組織のいずれかである可能性があるため、顧客を実装するテーブルはスーパータイプを参照する必要があります。
create table customers (
party_id integer primary key references parties (party_id) on delete cascade
-- Other attributes of customers
);
この決定は、この連絡先情報がどれほど重要であるか、どのくらいの頻度で変更されるか、電話番号を持つさまざまなタイプの人々の間でどれだけ重複する可能性があるかについての実際的な評価に基づく必要があると思います.
連絡先情報が揮発性であったり、アプリケーションにとって本当に中心的なものであったりする場合は、正規化を増やしたほうがよいでしょう。これは、さまざまな CUSTOMER、SUPPLIER、EMPLOYEE テーブル (など) が指すことができる PHONE_NUMBER テーブルを持つことを意味します。または、連絡先の種類、連絡先の個人 (顧客/サプライヤー/従業員) と連絡先(電話)。このようにして、従業員の自宅の電話番号を顧客記録の主要なビジネス番号にすることができ、それが変更された場合は、その連絡先が使用されるたびに 1 回変更されます。
一方、電話番号を保存しているのに、それらを使用せず、おそらく維持しない場合は、多くの時間と労力を費やしてモデリングし、この高度な機能をデータベースに組み込むことはできません。それだけの価値があり、顧客、サプライヤー、従業員、またはあなたが持っているものに関する、昔ながらの電話1、電話2、電話3、...の列を実行できます。これはデータベース設計としては不適切ですが、プロジェクトの優先順位を特定するために 80/20 ルールを適用している限り、システム開発の良い方法です。
要約すると、データが重要な場合は正しく行い、データが実際に重要でない場合は、それを平手打ちするか、できれば完全に除外します。
最も簡単な方法がおそらく最善です。スタッフ、顧客、またはサプライヤーがすべて電話番号、携帯電話番号、およびファックス番号の場所を持っていたとしても、それらのフィールドを各テーブルに配置するのがおそらく最善です。
しかし、そのようなフィールドが多ければ多いほど、ある種の「継承」または集中化を考慮する必要があります。他の連絡先情報と複数の電話番号がある場合は、これらの共通の値を一元化されたテーブルContactsに含めることができます。顧客、サプライヤーなどに固有のフィールドは、別のテーブルにあります。たとえば、Customer テーブルには、Contacts に戻る ContactID 外部キーがあります。