数据库设计 - 人员和组织

6 mysql postgresql database-design

我们正在构建的软件有“客户”。客户可以是个人或组织。

我真的很想为此创建一个最佳模式。

我有这些考虑。

  1. 一个人可以有一个或多个联系人(例如电话、电子邮件)
  2. 一个人可以有一个或多个地址
  3. 一个组织可以有一个或多个联系人(例如电话、电子邮件)
  4. 一个组织可以有一个或多个地址
  5. 一个组织可以有一个或多个与之相关的人员。

我希望应用程序能够适当地扩展,因此架构应该适合此选择。

我正在尝试实现以下目标。

SELECT * FROM Customers + a few joins.

1. Person | NULL | John Doe | Primary Organisation Contact | Primary Address

2. Organisation | Acme Ltd | Jane Doe | Primary Organisation Contact | Primary Address
Run Code Online (Sandbox Code Playgroud)

我应该如何创建一个最佳模式来关联上述内容?

我附上了一个粗略的 Visual Schema 层次结构 - 我知道我离得很远!!我确定我犯了错误。

http://snag.gy/yshxE.jpg

是否有可能根据是个人还是组织来获得一组已婚结果?

将个人和联系人/地址加入客户很简单,但您如何加入组织的主要联系人/地址?

有没有更简单的方法来实现我的蜘蛛图外观架构。??

Den*_*rdy 12

实际构建了几个与联系人和公司的应用程序的一些见解。

首先,您的大纲中缺少几个用例。在我多年来遇到的五颜六色的那些你不一定涵盖的:

  1. 有些组织有子组织,无论是部门、子公司,你都能说出它的名字。
  2. 有些人属于多个组织。就像 XYZ 的副总裁、ABC 的董事长和自雇的 CEO/顾问一样——希望每个人都是兼职。
  3. 有些人可以成为某个组织的联系人,但实际上并不属于该组织。例如,当一位知名顾问被临时聘为项目负责人时,他可能成为 XYZ 公司的主要联系人,但实际上并不属于它。
  4. 人也可以有子人。后者可以在同一家公司,也可以不在。例如,我们的 XYZ 副总裁可能更希望你通过他在那边的主要首席秘书,除非你就 ABC 的问题联系他,在这种情况下,他的助理;或者个人从事咨询工作——确实是一个非常忙碌的人,但也有这样的人。
  5. 一些组织没有与之关联的联系人。例如,这可能发生在输出列表潜在公司(销售人员)的应用程序中,其中的联系人尚未确定。
  6. 相反,联系人可以没有组织。比如消费者。

其次,由于您的提纲提到了客户,请注意客户可能是公司或(自然人)。您似乎根据您的图表涵盖了该部分,所以让我们继续。

如果您的下一步是添加与会计相关的任何内容,例如“订单”表,请注意任何与会计相关的内容都与特定时间点的公司、联系人、产品、价格等相关联。这些细节可以以各种丰富多彩的方式演变,一个常见的设计错误是创建完美的规范化设计并假设您只会根据需要更新表格。大不。如果税务员要求您打印发票,而您的 IT 说公司 XYZ 的价格为 P,而会计以 Q 的价格预订了公司 ABC,那么您就非常非常糟糕。并且不要让我开始归档/关闭的年度帐户和报告由于类似的设计错误而发生变化。

第三,要非常、非常、非常警惕您可能因过度规范化而引入的 UI/UX 问题。如果它不像典型用户手中的应用程序那样工作(阅读:Outlook),那么当乔获得新工作时,您将成为一名训练有素的秘书,无法将 XYZ 的每个员工的公司改为 ABC。我实际上已经看到这种情况发生,这不是一个漂亮的景象。(她保住了她的工作,以防你想知道。)

最后,不要让我开始合并不可避免的重复公司、联系人和许多其他不可能出现的丰富多彩的事情。保持这些放在心上,确保您的架构是非常非常宽容的,因为它发生。

现在……在实践中……

就个人而言,在这种情况下,我实际上对规范化视而不见。联系人/公司是其中一种情况,其中在学校教授的数据库设计和精美的规范化数据会导致资源占用、过于复杂的查询和极其复杂的 UI 形式的麻烦。无论如何,恕我直言,这不是正确的做法。

首先创建一个联系人表,填写诸如姓名、显示名称(如果需要)、公司名称、地址、电话、手机、电子邮件、email2、秘书姓名、秘书电话等字段。如果在 Outlook 中创建联系人时可用,它可能属于那里。

你会注意到我没有提到公司表。那是因为您通常不希望您的联系人表和公司表之间有任何强​​关联。如果需要,请添加一个,并在 company_name 之外添加一个 copmany_id。但是,在 delete set null 上使它成为外键。并确保在数据库级别与 company_name 保持非常非常松散的联系。在前端级别维护它——而不是其他任何地方。这将防止秘书无意中更改多个联系人的公司。

保持理智。任何可能合理地出现在列表中的东西(即 select * from contacts where ... limit 10)、被查询或经常有用的东西都应该在表中。没有加入,纳达。查询,遍历结果,完成。

如果您真的想要在那里添加更多垃圾,您有两种选择。

一种是创建一个 extra_contact_details 表。它可以是 EAV 表,也可以是公司名称、地址、电话等字段的全部负载,也可以是规范化的零碎内容。无论您采取哪种选择,都不要过度。当您创建 UI 时,这两种方法都会创建额外的(可能很复杂)查询,以及大量棘手的编程问题。这里绝对关键的是,如果你走这条路,一个一直使用 Outlook 的秘书需要理解它。

另一个,我实际上会预先推荐,是在联系人表中添加一个名为“extra_contact_details”的文本字段并开始使用它。根本没有标准化。一组主要细节。经常使用的次要细节。任何额外的纯文本。秘书收到。最终用户得到它。完毕。

最后,如果您需要在给定的时间点存储任何数据的一个版本,请确保复制任何关键细节的值——请阅读:无论您需要按插入时的原样打印它。

  • 您能否就规范化/非规范化数据库架构如何影响 UI/UX 说一两件事? (2认同)

Nei*_*gan 5

伙计,我希望数据库书籍能在第一章中介绍派对模型。

如果您的时间对您很有价值,请阅读 Hay 的 Enterprise Model Patterns 或 Silverston 的 Data Model Resource Book。可以在 Safari 上找到便宜的 em。

另请阅读表继承:

http://martinfowler.com/eaaCatalog/singleTableInheritance.html

如果您不喜欢空值,请使用类表继承。

一方代表人、组织、自动化代理:

Party
 id
 name

Individual : Party

Organization : Party

AutomatedAgent : Party
Run Code Online (Sandbox Code Playgroud)

客户是一方扮演的角色。例如,某人可能是您的供应商,您雇用他们作为员工,解雇他们,然后他们成为您的客户。

您可以根据他们是否向您下销售订单,或使用 PartyRole 将他们声明为客户来推导出他们的“客户”。

PartyRelationship 表示各方之间的关系:

PartyRelationship
 fromParty
 toParty
 fromDate
 toDate (nullable)

OrganizationContact : PartyRelationship

Employment : PartyRelationship
Run Code Online (Sandbox Code Playgroud)

地址代表物理地址和虚拟地址:

Address
 id

EmailAddress : Address
 emailAddress

WebAddress : Address
 url

TelephoneNumber : Address
 telephoneNumber

MailingAddress : Address
 suiteOrApartment
 street
 city
 zip
Run Code Online (Sandbox Code Playgroud)

人们有多种联系方式:

ContactMethod
 party
 address
 fromDate
 toDate (nullable)
 telephoneExtension (nullable)
Run Code Online (Sandbox Code Playgroud)

我强烈推荐使用像 Hibernate 这样的工具,因为它会让你的生活更轻松。


小智 1

像这样的事情怎么办?

addresses
    id              unsigned int(P)
    street1         varchar(50)
    street2         varchar(50)
    city_id         unsigned int(F cities.id)
    zip             varchar(6)
    lat             double
    lon             double

cities
    id              unsigned int(P)
    state_id        unsigned int(F states.id)
    name            varchar(50)

countries
    id              char(2)(P)
    iso3            char(3)(U)
    iso_num         char(3)(U)
    name            varchar(45(U)

customers
    id              unsigned int(P)
    parent_id       unsigned int(F customers.id) (Allow NULL)
    name            varchar(50) // John Doe, ABC Company, etc.

customers_addresses
    id                  unsigned int(P)
    customer_id         unsigned int(F customers.id)
    address_id          unsigned int(F addresses.id)

customers_email_addresses
    id                  unsigned int(P)
    customer_id         unsigned int(F customers.id)
    email_address_id    unsigned int(F email_addresses.id)

customers_phone_numbers
    id                  unsigned int(P)
    customer_id         unsigned int(F customers.id)
    phone_number_id     unsigned int(F phone_numbers.id)

email_addresses
    id              unsigned int(P)
    address         varchar(255)

phone_numbers
    id              unsigned int(P)
    area_code       char(3)
    exchange        char(3)
    station         char(4)
    extension       varchar(10)

states
    id              unsigned int(P)
    country_id      char(2)(F countries.id)
    code            char(2)
Run Code Online (Sandbox Code Playgroud)

电话号码和邮政编码适用于北美国家/地区,而对于其他国家/地区则必须进行更改。

以下是该customers表的一些示例数据:

+----+-----------+---------------+
| id | parent_id |     name      |
+----+-----------+---------------+
|  1 |      NULL | ABC Company   |
|  2 |      NULL | XYZ Company   |
|  3 |         1 | John Doe      |
|  4 |         1 | Jane Doe      |
|  5 |         2 | John Q Public |
|  6 |      NULL | JKL Company   |
+----+-----------+---------------+
Run Code Online (Sandbox Code Playgroud)

组织始终有一个NULLparent_id。个人可以有一个NULLparent_id(当他们不与组织关联时)或非NULLparent_id(当他们与组织关联时)。您的特定需求可能需要其他更改......

SQL 示例:

SELECT * FROM customers c
LEFT JOIN customers_addresses ca ON c.id = ca.customer_id
LEFT JOIN customers_email_address ce ON c.id = ce.customer_id
LEFT JOIN customers_phone_numbers cp ON c.id = cp.customer_id
LEFT JOIN cities ON ca.city_id = cities.id
LEFT JOIN states ON cities.state_id = states.id
LEFT JOIN countries ON states.country_id = countries.id
WHERE c.id = 1
Run Code Online (Sandbox Code Playgroud)