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

Database design 来自数据库中不同实体的相同数据-最佳实践-电话号码示例,database-design,database-normalization,Database Design,Database Normalization,考虑到一个涉及员工、客户和供应商的数据库系统,所有这些人都有多个可能的电话号码,您将如何以一种良好的标准化方式存储这些号码?我想了一下,合乎逻辑的方法并没有冲我而来。最直接的方法可能是最好的。即使员工、客户或供应商都有一个电话、手机和传真号码的位置,也最好将这些字段放在每张表上 但>,您拥有的字段越多,就越应该考虑某种“继承”或集中化。如果有其他联系信息,以及多个电话号码,您可以在一个集中的表Contacts上有这些公共值。特定于客户、供应商等的字段将在单独的表中。例如,Customer表将有

考虑到一个涉及员工、客户和供应商的数据库系统,所有这些人都有多个可能的电话号码,您将如何以一种良好的标准化方式存储这些号码?我想了一下,合乎逻辑的方法并没有冲我而来。

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

<强>但>,您拥有的字段越多,就越应该考虑某种“继承”或集中化。如果有其他联系信息,以及多个电话号码,您可以在一个集中的表Contacts上有这些公共值。特定于客户、供应商等的字段将在单独的表中。例如,Customer表将有一个ContactID外键返回到Contacts。

在大多数情况下

  • “员工”总是描述人
  • 有些顾客是人
  • 有些客户是企业 (组织)
  • “供应商”通常(总是?) 组织
  • 员工也可以是客户
  • 供应商也可以是客户
将员工电话号码、供应商电话号码和客户电话号码单独列成表格存在严重问题

  • 员工可以是客户。如果一个职员 电话号码发生变化,客户是否会这样做 电话号码也需要更新吗?您如何知道要更新哪一个
  • 供应商可以是客户。如果 供应商的电话号码发生变化,客户是否 电话号码也需要更新吗?您如何知道要更新哪一个
  • 您必须复制和维护约束,且无误 每一张桌子上的电话号码 储存电话号码
  • 同样的问题也出现在 客户的电话号码更改。现在 你必须检查一下 员工和供应商电话号码 也需要更新
  • 回答“谁的电话?” 电话号码是123-456-7890?”,你必须 看看不同的桌子,在哪里 “n”是不同的 与你打交道的各种各样的人。在里面 除了员工、客户和 供应商,想想“承包商的 电话、“潜在客户的电话”等
您需要实现一个超类型/子类型架构。(PostgreSQL代码,未经过严格测试。)

为了进一步扩展,实现“staff”的表需要引用person子类型,而不是party超类型。组织不能有员工

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

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

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

另一方面,如果你存储电话号码是为了好玩,而你不使用它们,也可能不会维护它们,那么花大量时间和精力建模并将这种复杂的功能构建到数据库中是不值得的,你可以做一些好的、老式的Phone1、Phone2、Phone3,。。。关于客户、供应商、员工或你拥有的东西的专栏。这是一种糟糕的数据库设计,但它是一种良好的系统开发实践,因为它应用80/20规则来确定项目优先级

总而言之:如果数据很重要,就把它做好,如果数据真的不重要,就把它放进去——或者更好的是,把它完全去掉。

by对MariaDB起作用,只有一个变化:“~”需要变成“LIKE”

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

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

关于在子类型表中使用虚拟列作为类型列,您的想法是否可能重复?@Arik:我不认为使用虚拟列作为“类型”列会起作用,不管它是在超类型表中还是在子类型表中。不能插入虚拟列,不能为其设置默认值,也没有表达式可以可靠地确定正确的值。这种行为可能取决于DBMS;我很确定PostgreSQL、SQL Server和Oracle都是如此。我刚刚尝试过,生成的虚拟列不能用作外键,但我成功地生成了一个存储列。我只在子类型表中使用它来强制使用单数类型,而不必显式地提供它。例如:创建表
person\u st
party\u id
int NOT NULL,
party\u type
char(1)始终作为('O')存储生成)
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);