数据库行中的标志,最佳实践
我出于好奇而问这个问题。 基本上我的问题是,当你有一个数据库需要一个行的条目,像旗帜一样的事情,最好的做法是什么? 一个很好的例子就是堆栈溢出的徽章或者bugzilla中的操作系统字段。 标志的任何子集可以为给定的条目设置。
通常,我做c和c ++的工作,所以我的直觉反应是使用一个无符号的整数字段作为一组位可以翻转…但我知道这是不是一个好的解决scheme,有几个原因。 其中最明显的就是可扩展性,对于我能拥有多less标志将会有一个硬性的上限。
我还可以考虑一些其他解决scheme,这些解决scheme可以更好地扩展,但会有性能问题,因为它们需要多个选项来获取所有信息。
那么,做这件事的“正确”方法是什么?
如果你确实需要从一组封闭的标志(例如stackoverflow徽章)中select一个无限的select,那么“关系方式”就是创build一个标志表和一个将这些标志与你的目标实体相关联的独立表。 因此,用户,标志和usersToFlags。
但是,如果空间效率是一个严重的问题,查询能力不是问题,那么一个未签名的面具几乎可以工作。
一般来说,我避免了位掩码字段。 他们很难在将来阅读,他们需要更深入的了解数据的知识。
关系解决scheme之前已经提出过。 鉴于你列出的例子,我会创build这样的东西(在SQL Server中):
CREATE TABLE Users ( UserId INT IDENTITY(1, 1) PRIMARY KEY, FirstName VARCHAR(50), LastName VARCHAR(50), EmailAddress VARCHAR(255) ); CREATE TABLE Badges ( BadgeId INT IDENTITY(1, 1) PRIMARY KEY, [Name] VARCHAR(50), [Description] VARCHAR(255) ); CREATE TABLE UserBadges ( UserId INT REFERENCES Users(UserId), BadgeId INT REFERENCES Badges(BadgeId) );
对于很多情况,它依赖于很多东西 – 比如你的数据库后端。 例如,如果您使用的是MySQL, SET数据types就是您想要的。
基本上,这只是一个位掩码,赋值给每一位。 MySQL最多支持64位值(意味着64个不同的切换)。 如果你只需要8个,那么每行只需要一个字节,这是非常可观的节省。
如果你真的在一个领域有超过64个值,你的领域可能会变得越来越复杂。 您可能想扩展到BLOB数据types,这只是MySQL没有固有的理解的原始位集。 使用这个,你可以创build任意数量的位字段,MySQL很乐意将其视为二进制,hex或十进制值,但是你需要。 如果您需要超过64个选项,请根据您的应用程序创build尽可能多的字段。 不足之处在于,很难使该领域的人类可读。 BIT数据types也被限制为64。
一个非常关系的方法
对于没有设置types的数据库,可以打开一个新表来表示为每个标志设置的实体集合。
例如,对于“学生”表,你可以有表格“注册学生”,“病假学生”,麻烦学生等。每个表将只有一列:student_id。 如果你想知道哪些学生是“注册”或“生病”,并且在每个DBMS中以相同的方式工作,这实际上是非常快的。
如果这些标志具有非常不同的含义,并直接在SQL查询或VIEWS中使用,那么使用多个BOOLEAN
types的列可能是一个好主意。
把每个标志放到一个额外的列中,因为无论如何你都会分别阅读和修改它们。 如果你想分组标志,只要给他们的列名一个共同的前缀,而不是:
CREATE TABLE ... ( warnings INTEGER, errors INTEGER, ... )
你应该使用:
CREATE TABLE ... ( warning_foo BOOLEAN, warning_bar BOOLEAN, warning_... error_foo BOOLEAN, error_bar BOOLEAN, error_... BOOLEAN, ... )
尽pipeMySQL没有BOOLEANtypes,但可以使用准标准TINYINT(1),并将其设置为0或1。
如果不仅仅是less数几个标志,或者将来可能会有这样的标志,我将在它们之间使用一个单独的标志表和一个多对多的表。
如果有一些标志,我永远不会在一个地方使用它们,我会使用一个SET()或位域或其他。 他们很容易阅读和更紧凑,但一个痛苦的查询,有时甚至更多的头痛与ORM。
如果只有几个标志 – 只会有几个标志 – 那么我只是做几个BIT / BOOLEAN / etc列。
如果你的数据库支持,我会build议使用BOOLEAN数据types。
否则,最好的方法是使用NUMBER(1)或等价的方法,并将限制有效值为(0,1)的列设置检查约束,如果需要的话可以为NULL。 如果没有内置types,则使用数字不会使用字符列。 (什么是真值?“T”或“Y”或“t”)
关于这一点的好处是你可以使用SUM()来计算TRUE行的数量。
SELECT COUNT(1), SUM(ActiveFlag) FROM myusers;