【发布时间】:2009-06-28 02:21:07
【问题描述】:
我正在为一个拥有多种“类型”用户的组织设计一个数据库。起初,我只创建了一个用户表。然而,虽然所有用户共享一些公共信息(名字、姓氏、用户名、密码等),但每种用户类型都需要一个或两个附加字段,这些字段并不适用于所有用户。虽然我可以创建这些额外的字段并将它们设置为 NULL,但我不希望这样做,因为这些字段是外键,它给我带来了问题。
这种情况通常如何处理?
谢谢!
【问题讨论】:
我正在为一个拥有多种“类型”用户的组织设计一个数据库。起初,我只创建了一个用户表。然而,虽然所有用户共享一些公共信息(名字、姓氏、用户名、密码等),但每种用户类型都需要一个或两个附加字段,这些字段并不适用于所有用户。虽然我可以创建这些额外的字段并将它们设置为 NULL,但我不希望这样做,因为这些字段是外键,它给我带来了问题。
这种情况通常如何处理?
谢谢!
【问题讨论】:
【讨论】:
您不创建包含大量 NULL 的大表的直觉是正确的。从存储/检索/维护的角度来看,以及从数据验证的角度来看(稍后会详细介绍),这是个坏主意。
两种最常见的方法:
1) 有一个包含所有公共字段的用户表,包括一个“userType”字段。然后为每个包含额外字段的用户类型创建一个单独的表。所有用户在用户表和一个或多个特定用户类型表中都有一行。这对于存储和快速登录来说是最规范和最有效的。这还允许您使用约束和外键来确保每种用户类型的所有必需信息都可用。
2) 有一个包含所有公共字段的用户表。有另一个名为 UserAttributes 的表,其中包含用户 ID、键和值的字段。特定用户的任何额外元数据都可以存储在此处。这样做的好处是不需要任何数据库管理来添加新的用户类型或要为每个用户类型存储的元数据。但是,它不允许您在数据库级别进行任何数据验证。
【讨论】:
因此,关系模型不支持“继承”,这可能有助于解决这个问题(尽管一些数据库引擎,例如 PostgreSQL,确实支持继承)。
所以,我首先要问自己——至少在某些情况下,不同类型的用户是否需要能够出现在相同的上下文中?如果是这样,那么您不能只是将“共同的列”复制并粘贴到多个表中(至少在不损害在这些情况下可以通过外键对单个表进行的完整性检查的情况下)。
第二个问题——一个用户曾经有可能担任多个角色吗?在许多情况下,这将是不寻常,但并非完全不可能,例如员工也可能是供应商或客户。
如果我无法对这些指示我的问题给出明确的答案,我会设置一个只有公共字段的用户表;并为供应商、员工、beta 测试人员、客户以及我可能为用户提供的任何其他类型和角色提供单独的表,每个表都有自己的专用列以及用户表上的外键以获取其余部分。
我意识到规范化模式现在已经过时了,但几十年来它们一直忠实地为我服务,我非常喜欢它们——我只在需要特定优化时才去规范化,而且这种情况很少发生想想!-)。
在这里可能有用的一种非规范化是用户表中的枚举列,指示每个特定用途的“主要”或“唯一”角色(如果我是足够急于从一开始就拥有它......;-)......但如果某些特定查询的性能需要它作为特定优化,我可能会等待添加它,而不是从开始(请注意,这是永远不要在查询中使用SELECT * FROM 的关键原因——如果您稍后使用ALTER TABLE 添加一列,那么SELECT * 就是会中断的那一位!-)。
【讨论】:
你没有说你是否使用高级语言,所以我只是举一个类似 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' (or such) ... 来获取用户。在那里,我希望您有某种 'id' 值来识别该行.
现在,只需执行 'SELECT * FROM permissions WHERE user_id = 'nnnn' (无论该用户的 id 是什么)。
如果用户只有 1 个权限集,那么您可以只拥有该 user_id 而无需额外的“id”列。
【讨论】: