具有多个类似实体的数据库最佳实践

时间:2014-01-15 13:07:49

标签: mysql database laravel-4 eloquent

我有一个非常简单的问题,关于2个选项,我不知道选择哪一个。我有实体,可以是"联系人"。 A'联系'可以有多个电子邮件地址,多个电话号码和多个地址。在我的数据模型中,我创建了实体联系人,它实体与电子邮件,电话和地址有1到n的实现。现在,表格电子邮件只包含字段" email"和"评论"并且手机具有类似的结构" phoneNumber"和"评论"。

enter image description here

它是否更好'要将它们保存在2个不同的表格中,或者我应该制作一个表格,请将其命名为“详细信息”。或者其他什么,用列'值','键入'和评论'例如,类型是'电子邮件'或者' phoneNumber'。

enter image description here

我正在使用L4和Eloquent模型我希望能够轻松编写一些方法,这些方法可以提供与不同表格相同的功能。但我觉得在不同类型的信息之间分配字符串是错误的。那样的感觉更容易犯错误。通过急切加载,即使我有2个表,我也希望不会有更多的查询。仅供参考,电话/电子邮件中的行数肯定会低于10.000。

我选择哪种类型会有所不同吗?你会怎么做?为什么?

感谢您的帮助,

亲切的问候

2 个答案:

答案 0 :(得分:2)

这取决于您打算如何处理这些字段。如果没有进一步的信息,我倾向于把它们放在一张桌子里。

你会遇到一些纯粹主义者,他们说电子邮件地址与电话号码不同,因此使用相同的字段来保存两者都是违反自然的行为。

我同意这种想法,当你对这两个领域所做的不同时。就像有人说有时这个字段包含电话号码,有时候它包含交易金额,当我们计算总余额时,我们将所有交易金额加起来但忽略那些电话号码,我会哭

但是,这种推理可以被视为荒谬的极端。问题实际上不是现实世界中两件事情是否有所不同,而是它们是否与我们系统的目的不同。就像我无法想象的那样,因为有时地址是邮局信箱,有时它是街道地址,有时它是公寓号等,我们必须为这些事情中的每一个都有单独的字段,而不仅仅是“地址线1” “和”地址第2行“。或者我们必须为“棕发人的名字与金色头发的人的名字”分别设置字段“,因为嘿,他们看起来不一样。我与用户进行了许多令人沮丧的对话,我试图向系统解释这个系统关注,一切都是“产品”,用户说不,不,你怎么可能说家具与文具一样?但如果我们在系统中做的是记录名称,手头数量和价格,我不在乎差异。等等。

事实上,我在软件开发中的一些最烦恼的时刻是当我意识到现实世界中两个不同的东西对系统来说真的是相同的,并且可以用一个表或一个代码块而不是许多代码来处理。就像沿线一样,我发现员工,供应商和客户都有姓名,地址,电话号码和电子邮件。所以现在不是在employee表中的所有这些字段以及vendor表中的所有相同字段以及customer表中的所有相同字段,而是创建一个我称之为“person”的字段并将所有常见内容放在那里,然后只是从其他表链接到它。因此,当有人出现并说现在我们必须处理外部地址时,我会更改一个表而不是三个表,如果我是智能的,则使用一个地址格式化函数而不是3个。

在这种情况下,您打算如何处理电话号码和电子邮件地址?可能主要是让用户输入它们然后再显示它们。我可以很容易地设想一个你永远不会检查哪个是哪个系统。在数据输入时,“联系信息类型”有一个下拉列表,在显示时显示联系人的类型以及联系人值,可能按联系人类型排序。如果您要发送自动电子邮件,可以选择type ='email'。

现在,如果您使用电子邮件地址作为连接字段从此表连接到另一个表,那将会有所不同,因为那时您的一半数据没有意义。

顺便说一句,如果您使用一个表,则需要一个代码来说明这是什么类型的联系人。我想你意识到了这一点。我建议你考虑创建一个包含代码及其定义的查找表,比如create table contact_type(contact_type_code char(2)primary key,contact_type_description varchar(40)),而不是硬编码程序中的联系人类型。或者更糟糕的是,将联系人类型的描述放在每个记录中,因此有时它会显示“电子邮件”,有时会显示“电子邮件”,有时会显示“电子邮件”,有时可能会显示“电子邮件”或“互联网”

对不起漫长的回答表示抱歉。

答案 1 :(得分:0)

这就是我通常为地址,联系人和电子邮件设计表格的方式:

示例地址类型可能是“Home”,“Mailing”,“Work”等。

address_types
    id                  varchar(15)(P)

city_idcounty_id都可以为NULL的原因是因为在弗吉尼亚州,地址位于城市或县内,但不是两者。因此,在应用程序级别,我强制执行这两个字段中的至少一个不能为NULL。

addresses
    id                  unsigned int(P)
    address_type_id     varchar(15)(F address_types.id)
    line1               varchar(50)
    line2               varchar(50)
    city_id             unsigned int(F cities.id) Default NULL
    county_id           unsigned int(F counties.id) Default NULL
    zip                 varchar(6)
    zip4                char(4) Default NULL
    lat                 decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL     lon                     decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL

您可能想要或不想要fips_number之类的内容以及其后列出的列。它们是美国政府使用的代码。

cities
    id                      unsigned int(P)
    state_id                unsigned int(F states.id)
    name                    varchar(50)
    lat                     decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL
    lon                     decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL
    fips_number             unsigned int // Default NULL
    census_code             unsigned int // Default NULL
    census_class_code       char(2)  // Default NULL
    gsa_code                unsigned int // Default NULL
    opm_code                unsigned int // Default NULL

城市和县之间存在多对多的关系,所以我们在这里定义。每列都是它的相应表的外键,它们一起形成主键。

cities_counties
    city_id             unsigned int \_ (P) (F cities.id)
    county_id           unsigned int /      (F counties.id)

示例联系类型可能是“Home”,“Work”,“Pager”,“Cell”等。

contact_types
    id                  varchar(15)(P)

您可能会或可能不想将电话号码分解为其组成部分。在这个例子中,它们被分解了。查询特定国家/地区代码,区号等中的数字的便捷方式

contacts
    id                  unsigned int(P)
    contact_type_id     varchar(15)(F contact_types.id)
    country_code        char(3) // Default to NULL
    area_code           char(3)
    exchange            char(3)
    station             char(4)
    extension           varchar(10) // Default to NULL

这将包含美国各州和地区的所有县,教区和其他类似的地理区域。

counties
    id                      unsigned int(P)
    state_id                unsigned int(F states.id)
    name                    varchar(50)
    fips_number             unsigned int // Default NULL

有关国家/地区代码的更多信息,请查看ISO 3166-1

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

示例联系类型可能是“主页”,“工作”等

email_types
    id                  varchar(15)(P)

电子邮件地址 - 您可能会也可能不想像我的例子那样将它们分开。

emails
    id              unsigned int(P)
    email_type_id   varchar(15)
    mailbox         varchar(255)
    domain          varchar(255)

有关州代码的更多信息,请查看ISO 3166-2

states
    id                      unsigned int(P)
    country_id              char(2)(F countries.id)
    code                    varchar(3)
    name                    varchar(45)
    fips_number             unsigned int // Default NULL

当然,你将它们绑定到一个实体:

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

customer_contacts
    id              unsigned int(P)
    customer_id     unsigned int(F customers.id)
    contact_id      unsigned int(F contacts.id)

customer_emails
    id              unsigned int(P)
    customer_id     unsigned int(F customers.id)
    email_id        unsigned int(F emails.id)