用户表到用户和用户首选项。这是正常化的吗?

时间:2014-08-27 20:44:48

标签: sql sql-server normalization

我有一个名为Users的表,其中包含越来越多的首选项。这些偏好可能包括ReligionId(这将关键到另一个包含宗教列表的表)。

偏好列表正在增长。我想将它从Users表拆分为2个表。我认为可行的策略是创建一个名为UserPreferences的单独表。我想知道这样做是否符合规范化规则。这是一个让事情变得更清晰的例证。

enter image description here

这是规范化的吗?还有更好的方法吗?所有评论都表示赞赏。

编辑:UserPreferences如何键入其他表:

enter image description here

5 个答案:

答案 0 :(得分:3)

有些人建议每行存储一个首选项。这称为实体 - 属性 - 值表,规范化。有人说EAV是更正常化的,"但他们错了。没有规范化规则鼓励EAV作为关系数据库中的设计。

如果所有首选项的所有值共享此首选项表中的单个列,则可以告诉它未正常化的一种实用方法是,您不能再对宗教查找表使用外键约束。您不能使外键约束仅限制特定首选项类型的行的值 - FK约束始终适用于表中的所有行。
基本上,Entity-Attribute-Value打破了SQL对约束的支持。

唯一的标准化设计是为每个不同的首选项定义一个单独的列。然后,您可以定义适合该首选项类型的数据类型和约束。

如果您真的想了解关系和规范化,请阅读SQL and Relational Theory: How to Write Accurate SQL Code by C. J. Date

每列代表一组中的选择。集合可以是整数集合,也可以是宗教集合或电子邮件地址集合。表格中的一行是一组"组合在一起的"例如,一个给定的用户有一个名字,一个生日,一个宗教和一个电子邮件地址,所以这些价值被一起匹配成一行,他们一起描述世界上存在的东西,即一个人与那些属性。

这意味着在每一行中,您为每个列选择一个值,即从每个引用的组件集中选择一个值。并且每列包含来自一组的值。在宗教专栏中,您只能选择宗教信仰,您也可以将喜爱的颜色和母亲的婚前姓名和鞋码放在同一列中。

这就是为什么EAV从关系角度来看是虚假的,因为它将来自任何和所有不同属性的值混合到同一列中。它更像是电子表格而不是数据库。并非我说关系是存储数据的唯一方法。只是如果您要求EAV是否正常化,并且规范化假定数据是关系的先决条件,那么不,EAV不是关系的,因此无法规范化。

答案 1 :(得分:1)

至少你可以拥有用户和偏好设置。用户和偏好之间应该存在一对多的关系。一个用户可以有许多偏好。您还可以将电子邮件地址拆分为另一个表 - 以便一个用户可以拥有多个电子邮件地址 - 您可以使用一个标记来表示主要电子邮件地址。 DDL看起来像:

    create table Users
    (
       UserId int,
       Age int
    )

    create table Preferences
    (
        PreferencesId int,
        UserId int,
        ReligionId int,
        PersonalDescription varchar(2000),
        HairColor int
    )

    create table EmailAddresses
    (
        EmailId int,
        UserId int,
        EmailAddress varchar(100),
        IsPrimary bit
    )

    create table Religion
    (
        ReligionId int,
        Name varchar(200)
    )

Insert into Religion (ReligionId, Name) Values (1, 'Jediism')
Insert into Religion (ReligionId, Name) Values (2, 'Sithism')
Insert into Religion (ReligionId, Name) Values (3, 'Yuuzhan Vong')
Insert into Religion (ReligionId, Name) Values (4, 'Pinacism')

Insert into Users (UserId, Age) Values (1, 30)
Insert into Users (UserId, Age) Values (2, 18)

Insert into Preferences (PreferencesId, UserId, ReligionId, PersonalDescription) values (1, 1, 1, 'a description')
Insert into Preferences (PreferencesId, UserId, ReligionId, PersonalDescription) values (2, 1, 4, 'another description')
Insert into Preferences (PreferencesId, UserId, ReligionId, PersonalDescription) values (3, 1, 4, 'even another description')

Email table omitted

答案 2 :(得分:1)

将单个表拆分为两个表是没有意义的。只有你这样拆分的时候一些用户根本就没有偏好

创建新列 - 每当新的偏好出现时 - 不是一个好主意。

如果您认为偏好设置将来会增长,您可以使用以下方法 -

enter image description here

答案 3 :(得分:1)

规范化的userPreference表将包含userID,preferenceID和preferenceValue。首选项会在一行中列出您的所有首选项(电子邮件,年龄等),并将ID作为PK,以及您要添加的任何描述性信息。


由于首选项值的数据类型不同,您可以将其声明为字符串/ varchar,或者,如果您确实需要,可以为不同的数据类型设置不同的首选项值,例如'prefValInt','prefValChar'等。,只有一列包含值。

我通常只使用一个字符串。


现在,如果您想强制引用完整性,就像上面的示例一样,您又回到将每个首选项ID放在不同的列中,因此您可能希望将这些首选项保留在主表和所有其他首选项中在查找表中。


任何需要RI强制执行的列(例如religionID和hairColorID)都可以放在用户表中,也可以放在与用户表具有1-1关系的userPreferenceRI表中。任何没有强制执行RI的列,例如age,dateOfBirth,accountBalance,emailBody都可以进入userPreference表,其中PK是userID和preferenceID以及与用户表的1-many关系。

HTH


您还应该考虑优惠存在的可能性。对于必需的字段,例如ageAtPointInTime或dateOfBirth,您应该将它们存储在用户表中。对于稀疏填充的首选项(大多数没有答案),您应该将它们放在查找表中。

    user: userID, requiredFld1, requiredFld2
    preference: preferenceID, preferenceName, preferenceDescription
    userPreference: userID, preferenceID, userPreferenceVal as varchar(100) 
(list of columns never changes)
    userPreferenceRelated: userID, religionID, hairColorID, otherPreferenceID 
(list of columns grows over time)

答案 4 :(得分:1)

如果您说您的偏好越来越多,那么我建议您为首选项创建一个新表并将FKey添加到UserPreferences

用户表 - 用户ID,电子邮件,年龄,alternateemail ...

首选项表 - Preferenceid,preference_Value,active,required

用户首选项表 - userid,preferenceid,preference_data

现在,您可以在“首选项”表中获得不断增长的首选项列表,并将其连接到UI,两列 - “活动”和“必需”将帮助您轻松地从后端控制表单。

在用户首选项表中,您只需使用用户ID引用首选项ID,并存储用户为该首选项输入的数据。

我希望这很清楚。