数据库中不同实体的相同数据 – 最佳实践 – 电话号码示例
一个相当简单的问题,如果我有一个处理员工,客户和供应商的系统,所有这些系统都有多个可能的电话号码,那么你会如何以一种很好的规范化的方式来存储这些数字呢? 我有一个想法,逻辑的方式是不跳出来对我。
在多数情况下 。 。 。
- “工作人员”总是描述人。
- 有些客户是人。
- 有些客户是企业(组织)。
- “供应商”通常是(总是)组织。
- 员工也可以是客户。
- 供应商也可以是客户。
将工作人员电话号码,供应商电话号码和客户电话号码分开的表格存在严重的问题。
- 员工可以是客户。 如果员工电话号码发生变化,客户电话号码是否也需要更新? 你怎么知道哪一个更新?
- 供应商可以是客户。 如果供应商的电话号码发生变化,客户电话号码是否也需要更新? 你怎么知道哪一个更新?
- 您必须复制并维护存储电话号码的每个表格中的电话号码的限制。
- 当客户的电话号码改变时,也会出现同样的问题。 现在您必须检查是否需要更新员工和供应商的电话号码。
- 要回答“谁的电话号码是123-456-7890?”的问题,你必须查看“n”个不同的表格,其中“n”是你处理的不同“种类”的数量。 除了员工,客户和供应商之外,还要考虑“承包商的电话”,“潜在客户的电话”等。
你需要实现一个超types/子types模式。 (PostgreSQL代码,没有经过严格的testing。)
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);
为了进一步扩展这一点,实施“员工”的表格需要参考人员子types,而不是参与者超级types。 组织不能成为员工。
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 );
如果供应商只能是组织,而不是个人,那么实施供应商的表格就会以类似的方式参考组织的子types。
对于大多数公司来说,客户既可以是个人也可以是组织,因此实施客户的表格应该引用超types。
create table customers ( party_id integer primary key references parties (party_id) on delete cascade -- Other attributes of customers );
我认为这个决定需要基于对这个联系信息的重要性的实际评估,这个信息的改变频率以及不同types的电话号码之间可能存在多less重叠。
如果联系信息对于应用程序来说是不稳定的和/或非常重要的,那么更多的标准化可能会更好。 这意味着您的各种客户,供应商,员工表(等)可能指向一个PHONE_NUMBER表,或者更有可能在联系types,联系人(客户/供应商/员工)和联络点(电话)。 通过这种方式,您可以将员工的家庭电话号码作为客户logging的主要业务号码,如果发生变化,则每次使用该联系点时都会更改一次。
另一方面,如果你在存储电话号码,而你没有使用它们,并且可能不会维护它们,那么花费大量的时间和精力build模,并将这种复杂性构build到数据库中,不值得,你可以在客户,供应商,员工或者你有什么好的,老式的Phone1,Phone2,Phone3,…上做这个栏目。 这是不好的数据库devise,但它是一个很好的系统开发实践,因为它将80/20规则应用于确定项目优先级。
所以总结一下:如果数据很重要,那么做对,如果数据并不重要,就把它放在一边 – 或者更好,然后把它放在一边。
最直接的方法可能是最好的。 即使员工,客户或供应商都有电话,手机和传真号码的位置,但最好将这些字段放在每张桌子上。
但是 ,你拥有的领域越多,就越应该考虑某种“inheritance”或集权。 如果还有其他联系信息以及多个电话号码,则可以在集中的表格“ 联系人”中使用这些常用值。 作为客户,供应商等特定的字段将在单独的表格上。 例如,客户表将有一个ContactID外键返回到联系人。