问题 MySQL问题 - 如何处理多种类型的用户 - 一个表还是多个?


我正在为具有多个“类型”用户的组织设计数据库。起初,我只创建了一个用户表。但是,虽然所有用户共享一些公共信息(名字,姓氏,用户名,密码等),但每种用户类型都需要一个或两个不适用于所有用户的附加字段。虽然我可以创建这些附加字段并将它们设置为NULL,但我不希望这样做,因为字段是外键并且它对我造成了问题。

这种情况通常如何处理?

谢谢!


3516
2018-06-28 02:21


起源



答案:


你没有创建一个包含大量NULLS的大表的直觉就是正确的。从存储/回溯/维护的角度来看,这是一个坏主意,以及数据验证的观点(稍后将详细介绍)。

最常见的两种方法:

1)拥有一个包含所有公共字段的用户表,包括“userType”字段。然后为每个包含额外字段的用户类型提供单独的表。所有用户在users表和一个或多个特定用户类型表中都有一行。这是存储和快速登录的最规范化和最有效的方法。这还允许您使用约束和外键来确保每种用户类型的所有必需信息都可用。

2)拥有一个包含所有公共字段的用户表。还有一个叫做类似的表 UserAttributes 具有userid,key和value的字段。特定用户的任何额外元数据都可以存储在此处。这具有以下优点:不需要任何数据库管理来添加要为每个用户类型存储的新用户类型或元数据。但是,它不允许您在数据库级别进行任何数据验证。


9
2018-06-28 02:43



谢谢,dj_segfault。我选择了#1选项,并创建了一个用户表,其中包含公共字段和userType字段,该字段由其他更“特定”的用户表引用。 NULLS肯定会引起头痛,我很高兴现在摆脱他们! - littleK
纠正我,如果我错了(它发生了很多)但是如果有人以这样的方式存储他们的数据,其中公共字段处于“用户”级别,如fname,lname,email,pw ......等等。没有办法通过一个查询获得更多用户特定信息吗?它总是需要基于usertype的第二个查询来获取更具体的信息。 - ackerchez
@ackerchez当然有。获取所有信息所需要做的就是将所有用户类型特定的用户表与主用户表内连接,无论您的UID是什么。不适用于该用户类型的表的列将为null。 - dj_segfault


答案:


你没有创建一个包含大量NULLS的大表的直觉就是正确的。从存储/回溯/维护的角度来看,这是一个坏主意,以及数据验证的观点(稍后将详细介绍)。

最常见的两种方法:

1)拥有一个包含所有公共字段的用户表,包括“userType”字段。然后为每个包含额外字段的用户类型提供单独的表。所有用户在users表和一个或多个特定用户类型表中都有一行。这是存储和快速登录的最规范化和最有效的方法。这还允许您使用约束和外键来确保每种用户类型的所有必需信息都可用。

2)拥有一个包含所有公共字段的用户表。还有一个叫做类似的表 UserAttributes 具有userid,key和value的字段。特定用户的任何额外元数据都可以存储在此处。这具有以下优点:不需要任何数据库管理来添加要为每个用户类型存储的新用户类型或元数据。但是,它不允许您在数据库级别进行任何数据验证。


9
2018-06-28 02:43



谢谢,dj_segfault。我选择了#1选项,并创建了一个用户表,其中包含公共字段和userType字段,该字段由其他更“特定”的用户表引用。 NULLS肯定会引起头痛,我很高兴现在摆脱他们! - littleK
纠正我,如果我错了(它发生了很多)但是如果有人以这样的方式存储他们的数据,其中公共字段处于“用户”级别,如fname,lname,email,pw ......等等。没有办法通过一个查询获得更多用户特定信息吗?它总是需要基于usertype的第二个查询来获取更具体的信息。 - ackerchez
@ackerchez当然有。获取所有信息所需要做的就是将所有用户类型特定的用户表与主用户表内连接,无论您的UID是什么。不适用于该用户类型的表的列将为null。 - dj_segfault


因此,关系模型不支持“继承”,这可能有助于解决这个问题(尽管一些数据库引擎,如PostgreSQL,确实支持继承)。

所以,我首先会问自己 - 不同类型的用户是否需要能够出现在同一个环境中,至少在某些情况下?如果是这样,那么你不能只将“共同列”复制并粘贴到多个表中(至少不会影响在这些情况下通过外键到单个表中可以获得的完整性检查)。

第二个问题 - 是吗? 曾经 用户可以持有多个角色吗?在许多情况下,它会 异常 但并非完全不可能,例如员工也可能是供应商或客户。

如果我对这些问题没有得到明确答案,那么我会设置一个只包含公共字段的用户表;以及供应商,员工,beta测试人员,客户以及我可能为用户提供的任何其他类型和角色的单独表格,每个表格都有自己的专用列和用户表上的外键来接收其余的。

我意识到规范化的模式现在已经不合时宜了,但它们已经忠实地为我服务了几十年而且我对它们非常喜欢 - 我只是在需要特定优化时才进行非规范化,并且它发生的次数比人们想象的要少! - )。

这里可能有用的一个有点非规范化的是用户表中的枚举列,表示每个特定用途的“主要”或“唯一”角色(如果我足够强大,它可以是可空的,并且可能在开始时统一为空。从头开始......; - )......但是如果某些特定查询的性能需要它作为特定的优化,我可能会等待添加它,而不是从一开始就设计架构(请注意,这是永不使用的关键原因 SELECT * FROM 在您的查询中 - 如果您 ALTER TABLE 稍后再添加一列 SELECT * 是一个会破坏的! - )。


4
2018-06-28 02:36



亚历克斯,非常感谢你的回应,这对我很有帮助。我做了你的建议,并设置了一个包含公共字段的表,以及每个用户类型的其他非公共字段的单独表。我也在users表中添加了一个角色枚举列。谢谢! - littleK


这是着名的规范化问题。

看一下这篇文章或其他类似的文章,试着找到一个符合业务需求的答案。

规范化或不规范化


1
2018-06-28 02:37



非常感谢这篇文章,发现它非常有用。关于这个话题,我有很多问题,并且回答了很多问题。谢谢! - littleK
要给+1,但链接不再工作。你想更新吗? - Gabriel C. Troia
@Gabriel哇 - 我不知道它是否有效。我已将其更新为正确的文章 - Brian
@Brian你这个男人! :) - Gabriel C. Troia


你没有说你是否使用了高级语言,所以我只是给出一个类似DB的例子的一般例子:

数据库设计很难。所以,这将是一个快速而简单的答案。

您的问题是关于数据关系和数据库设计的基本问题。搜索一些基本的操作指南来帮助回答这个问题。考虑如何对信息进行分组,并将“返回”链接到其他集(表)中的主集(表)可能会有所帮助。

因此,用户是用户 - 这就是你的桌子。它应包含与用户关联的主要,公共元素(列)数据。

然后,该另一组信息(例如,许可或某事)是另一个表。

只需确保此其他表具有一个值(列),该值指向它所引用的用户。您可能希望告诉您的数据库在它们之间创建一个“索引”(以提高查找性能等)

例如,用户的一种“许可”表:

  - integer "id"        <--- unique, index column, auto-increment
  - integer "user_id"   <--- this is which user this belongs
  - ...
  - Boolean "can_write"         <--- example data column
  - Boolean "can_read"          <--- example data column
  - Boolean "can_reboot_system" <--- example data column
  - etc, whatever you want

所以,你可以“SELECT * FROM user_table WHERE first_name ='joe'(或类似)...来获取用户。在那里,我希望你有一些'id'值来识别那一行。

现在,只需执行'SELECT * FROM permissions WHERE user_id ='nnnn'(无论用户的id是什么)。

如果用户只有1个权限集,那么您可以拥有该user_id而不需要额外的“id”列。


0
2018-06-28 02:45



joej-我从未考虑过在数据库级别表达权限。当然看起来是个好主意。现在,我只有一个引用用户的“角色”表(然后我用php代码强制执行该角色)。我喜欢你的想法,我打算更多地考虑使用它。谢谢! - littleK