与大多数应用程序一样,我的“用户”表描述了可以登录的实体。它有他们的别名,他们的电子邮件地址,他们的盐渍密码哈希,以及所有常见的候选人。
然而,随着我的应用程序的增长,我需要越来越多的特殊情况“标志”,我通常只是停留在用户表中。就像他们最近的月度电子邮件是否已经传输一样,他们是否已经解散教程弹出窗口,他们点击了“我很棒”按钮等多少次等等。
我开始有相当多的这些字段,而且我所处理的大多数网页都不需要这些标记中的大部分。
在users表中保留所有这些标志有什么问题吗?有什么地方可以放置它们吗?创建与users表具有1:1关系的其他表会在我需要时检索数据时提供额外的开销吗?
另外,我使用Hibernate作为我的ORM,我担心为这些信息创建一堆额外的表意味着我还必须弄脏我的用户域对象。建议?
答案 0 :(得分:4)
有几种常见的解决方案:
EAV
在子表中每行存储一个标志,引用用户行,标志名称和值。缺点:无法保证每个标志存在一行。需要为标志名称定义另一个查找表。用其所有标志重新构建用户记录是一个非常昂贵的查询(需要每个标志的连接)。
位字段
在一个长二进制列中每位存储一个标志。在应用程序代码中使用位掩码来解释标志。缺点:人为限制标志数量。当它变得过时时难以丢弃旗帜。更难以更改标志值,搜索特定标志值,或基于标志值进行聚合,而不会使位操作符混淆。
规范化设计
每个标记存储一个BIT
列,全部在Users表中。从关系理论和规范化的角度来看,最“正确”的设计。缺点:添加标记需要ALTER TABLE ADD COLUMN
。此外,您可能会超出您的RDBMS品牌支持的列数或行大小。
答案 1 :(得分:2)
我会说更好的设计是这样的:
create table users (
id integer primary key,
user varchar(32) unique
)
create table flags (
id integer,
flagname varchar(32),
flagval char(1)
)
是id + flagname的主键。标志条目然后看起来像:
1, 'administrator', 'Y',
1, 'editor', 'Y',
2, 'editor' 'Y'
等等。我创建了一个视图来访问连接表。
答案 2 :(得分:2)
有趣的是,看到最疯狂的答案是唯一一个获得赞成的人。
这个问题没有包含足够的信息来实际给出明智的答案。
首先,它没有说明问题是关于数据库的某些逻辑设计,还是数据库的某些物理设计。
如果问题是关于逻辑设计的,那么答案很简单:永远不要在逻辑设计中包含布尔值。关系模型已经有一种表示是/否信息的方式(凭借封闭世界的假设):即某种关系中某些元组的存在。
如果问题是关于物理设计,那么任何合理的答案都必须依赖于其他信息,例如更新频率,查询频率,查询的数据量等等。这些都没有提供,使得问题无法回答。 / p>
修改
“关系模型只规定了一种类型,BOOLEAN(所有类型中最基本的类型)。” - C. J. Date,SQL and Relational Theory(2009)。“
该回复当然必然会出现。
但是这个引用是否真的说类型布尔值应该可以包含在某种关系类型中?或者引用(或者更好,它出现的更大的文本片段)只是说boolean类型的存在是不可避免的,因为否则系统无法为相等运算符的任何调用返回结果,并且它是否存在“规定”的等式运算符?)
IOW,类型布尔值是否可以包含在关系类型中,或者应该键入布尔值是否可用,否则我们无法定义单一的DML语言来操作数据库?
日期也在记录中(略微转述)“如果有N种方式来表示信息,N> 1,那么还有> 1套要学习的运算符,以及> 1种方式开发人员犯错误,“> 1套运算符供DBMS开发人员实施,以及> 1种方式让DBMS开发人员犯错”。
编辑编辑
“日期说”关系属性可以是任何类型。“他没有说一个属性可以是除boolean之外的任何类型”
你已经很好地阅读了日期。
Date绝对不会说的另一件事是属性不能是关系类型的。恰恰相反。然而,有一个广泛的共识,我知道甚至Date也有共识,即拥有包含关系类型属性的基础relvars可能是一个坏主意。
同样,Date也没有说在基本关系类型中包含布尔属性是个好主意。他对这个问题绝对保持沉默。我表达的意见是我的。我不认为我给人的印象是我在最初写的内容中表达了别人的意见。
表示“任何给定命题的真实性(或虚假性)”可以通过在某个relvar的关系值中包含/省略元组来完成(至少在逻辑上!)。现在,能够从某些给定的relvar的值中包含/排除某些给定的元组肯定是基本的。鉴于此,没有必要通过使用boolean类型的属性来表示任何给定命题的真实性(或虚假性)(逻辑上!)。还有什么你会使用boolean类型的属性,但是明确地说某些proprosition是true还是false?
答案 3 :(得分:1)
如果您真的只需要在几页上提供这些信息,为什么不在桌面上放一个&每个国旗的关系?该表中记录的存在设置该位,选择null
是未设置的位。
然后还可以通过为每次点击添加记录来完成令人敬畏的点击次数(这解决了更新用户表中记录中计数的竞争问题):
select count(*) from AwesomeClicks where userid = 1234
在userid
字段上使用唯一约束来获取仅位信息(真实标志,而不是上例中的计数)。
select userid from DismissedTutorialPopup where userid = 1234
这将导致1234(设置标志)或null(未设置标志)。
此外,通过添加CreateDate
字段,您可以在设置标志等时存储。
答案 4 :(得分:1)
有些人似乎并不喜欢这种模式,但是我已经开发了一种在base64字符串上进行二进制比较的方法,因此我可以在单个varchar字段中处理几乎无限数量的标志。 (技术上每个角色6个)
我承认对这种技术感到沮丧的是,几乎不可能从数据库中读取它们。但它对我有用。我的应用程序中定义的标志如下:
public class Flags
{
public const string Flag1 = "1";
public const string Flag2 = "2";
public const string Flag3 = "4";
public const string Flag4 = "8";
public const string Flag5 = "g";
public const string Flag6 = "w";
public const string Flag7 = "10";
// ... etc ...
}
答案 5 :(得分:0)
****将所有这些标志保存在users表中是否有任何问题?****
您好我不确定您当前使用的是哪个Db,但如果您使用的是SQL服务器,请确保行大小不会是8060字节。 (最大行数8060)。
MAX行大小
SQLserver 2005 - 8060字节 MYSQL - 8052字节 Oracle 9i - 255000字节。