数据库中不同实体的相同数据 - 最佳实践 - 电话号码示例

时间:2011-03-28 23:18:37

标签: database-design database-normalization

鉴于一个数据库系统处理员工,客户和供应商,所有这些都有多个可能的电话号码,您将如何以良好的规范化方式存储这些数字?我有一点思考,逻辑方式不是跳出来。

4 个答案:

答案 0 :(得分:24)

在大多数情况下。 。

  • “工作人员”总是描述人。
  • 有些客户是人。
  • 有些客户是企业 (组织)。
  • “供应商”通常(总是?) 的组织。
  • 员工也可以是客户。
  • 供应商也可以是客户。

使用单独的员工电话号码,供应商电话号码和客户电话号码表存在严重问题。

  • 员工可以是客户。如果是员工 电话号码变更,做客户 电话号码还需要更新吗?你怎么知道要更新哪一个?
  • 供应商可以是客户。如果一个 供应商的电话号码变更,做客户 电话号码还需要更新吗?你怎么知道要更新哪一个?
  • 您必须复制并维护没有错误的约束 对于每个表中的电话号码 存储电话号码。
  • 当a时出现同样的问题 客户的电话号码更改。现在 你必须检查是否 员工供应商电话号码 还需要更新。
  • 回答“谁的电话 号码是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);

为了进一步扩展这一点,实现“员工”的表需要引用人员子类型,而不是派对超类型。组织不能上班。

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
);

答案 1 :(得分:0)

我认为这个决定需要基于对这些联系信息的重要程度,变化频率以及不同类型的电话号码人之间可能存在多少重叠的实际评估。

如果联系信息是易变的和/或对应用程序非常重要,那么更多的规范化可能会更好。这意味着拥有一个PHONE_NUMBER表,您的各种CUSTOMER,SUPPLIER,EMPLOYEE表(等)可以指向 - 或者更可能通过联系人类型,联系人(客户/供应商/员工)之间的某种三向交叉引用联络点(电话)。这样,您可以将员工的家庭电话号码作为其客户记录的主要业务编号,如果更改,则每次使用该联系点时都会更改一次。

另一方面,如果您存储电话号码并且不使用它们并且可能不会维护它们,那么花费大量时间和精力建模并将这种复杂性构建到您的数据库是不值得的,你可以在CUSTOMER,SUPPLIER,EMPLOYEE或你有什么好的,老式的Phone1,Phone2,Phone3,...列。这是糟糕的数据库设计,但它是良好的系统开发实践,因为它将80/20规则应用于识别项目优先级。

总而言之:如果数据很重要,那就做对了,如果数据真的不重要,只需将其打入 - 或者更好,完全不用了。

答案 2 :(得分:0)

answerMike Sherrill 'Cat Recall'仅在MariaDB上起作用,只有一个更改:'〜'必须变成'LIKE'。

这是他的示例在MariaDB上测试过。我还对使用单词而不是单个字符描述的类型进行了asked about here的更改。

create table parties (
    party_id integer not null unique,
    party_type varchar(20) not null check (party_type in ('individual', 'organization')),
    party_name varchar(50) not null unique,
    primary key (party_id, party_type)
);

insert into parties values (1,'individual', 'Mike');
insert into parties values (2,'individual', 'Sherry');
insert into parties values (3,'organization', 'Vandelay');

-- For "persons", a subtype of "parties"
create table person_st (
    party_id integer not null unique,
    party_type varchar(20) not null default 'individual' check (party_type = 'individual'),
    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, 'individual', 72);
insert into person_st values (2, 'individual', 60);

-- For "organizations", a subtype of "parties"
create table organization_st (
    party_id integer not null unique,
    party_type varchar(20) not null default 'organization' check (party_type = 'organization'),
    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, 'organization', '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 varchar(10) not null default 'work' check
        (phone_type in ('work', 'home', 'emergency', 'business', 'mobile')),
    -- Phone numbers in the USA are 10 chars. YMMV.
    phone_number char(10) not null check (phone_number like '[0-9]{10}'),
    primary key (party_id, phone_type)
);

insert into phones values (1, 'home', '0000000000');
insert into phones values (1, 'mobile', '0000000001');
insert into phones values (3, 'home', '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.
-- Inserting and Updating with Views - MariaDB Knowledge Base https://mariadb.com/kb/en/library/inserting-and-updating-with-views/
--
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);

答案 3 :(得分:-1)

最直接的方式可能是最好的。即使员工,客户或供应商都有电话,手机和传真号码的位置,最好将这些字段放在每张桌子上。

但是,你拥有的这些字段越多,就应该考虑更多的“继承”或集中化。如果有其他联系信息以及多个电话号码,您可以在集中式表 Contacts 上拥有这些常用值。特定于客户,供应商等的字段将位于单独的表中。例如,Customer表将具有一个返回Contacts的ContactID外键。