2008-09-24 24 views
34

我在问好奇。基本上我的问题是,当你有一个数据库需要一个行条目来让事情像标志一样行事时,最佳实践是什么?一个很好的例子就是堆栈溢出的徽章或bugzilla中的操作系统字段。可以为给定条目设置标志的任何子集。数据库中的标志行,最佳实践

通常,我做C和C++的工作,所以我的直觉反应是使用无符号整数字段作为可以翻转的一组位...但我知道这不是一个好的解决方案,原因有几个。其中最明显的是可扩展能力,对于我可以拥有多少标志将会有一个硬性上限。

我也可以考虑一些其他解决方案,它们的规模会更好,但会有性能问题,因为它们需要多个选项才能获取所有信息。

那么,什么是“正确”的方式来做到这一点?

回答

26

如果你确实需要从一组关闭的标志(例如stackoverflow徽章)中选择一个无限选项,那么“关系方式”就是创建一个标志表和一个将这些标志与你的目标实体相关联的独立表。因此,用户,标志和usersToFlags。

但是,如果空间效率是一个严重问题并且查询能力不足,那么未经签名的掩码几乎可以工作。

+11

只对未签名的掩码发出警告。如果您必须编写查询来过滤设置了特定位的行,那么当行数变大时,您的性能将受到严重影响,因为where子句中的逻辑和/或操作无法有效地使用索引。 – JohnFx 2008-10-01 22:26:26

4

对于很多情况下,它取决于很多东西 - 比如你的数据库后端。例如,如果您使用的是MySQL,则SET datatype正是您想要的。

基本上,它只是一个位掩码,赋值给每个位。 MySQL最多支持64位值(意味着64个不同的切换)。如果你只需要8个,那么每行只需要一个字节,这是非常可观的节省。

如果您在一个字段中拥有超过64个值,那么您的字段可能会变得更加复杂。您可能想扩展到BLOB数据类型,这只是MySQL没有内在理解的原始位集。使用这种方法,您可以创建任意数量的位字段,MySQL很乐意将其视为二进制,十六进制或十进制值,但是您需要。如果您需要超过64个选项,请根据您的应用程序创建尽可能多的字段。缺点是很难使该领域的人类可读。 BIT datatype也限制为64.

+0

不是我会做的,但它是位掩码解决方案的一个很好的实现。 – 2008-09-24 01:23:31

28

一般来说,我避免了位掩码字段。他们很难在将来阅读,他们需要更深入的了解数据。

之前已经提出了关系解决方案。给你介绍的例子,我会创造这样的事情(在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) 
); 
+1

接受答案的好例子,谢谢。 – 2008-09-24 03:59:09

1

如果有不仅仅是几个标志的更多,或者可能在将来是如此,我将使用一个单独的标志表和它们之间的多对多表。

如果有少数的标志,我永远不会在WHERE中使用它们,我会使用SET()或位域或其他。它们易于阅读和更紧凑,但是使用ORM查询有时甚至更令人头痛。

如果只有几个标志 - 并且只有成为几个标志 - 那么我只需制作几个BIT/BOOLEAN/etc列。

2

如果标志具有非常不同的含义并直接用于SQL查询或VIEWS,那么使用BOOLEAN类型的多列可能是一个好主意。

将每个标记放入一个额外的列中,因为无论如何您都会分别读取和修改它们。如果你想组的标志,只是给他们列名的共同的前缀,即代替:

CREATE TABLE ... (
    warnings INTEGER, 
    errors INTEGER, 
    ... 
) 

你应该使用:

CREATE TABLE ... (
    warning_foo BOOLEAN, 
    warning_bar BOOLEAN, 
    warning_... 
    error_foo BOOLEAN, 
    error_bar BOOLEAN, 
    error_... BOOLEAN, 
    ... 
) 

尽管MySQL没有布尔类型,你可以使用准标准TINYINT(1),并将其设置为0或1.

1

如果您的数据库支持此操作,我会推荐使用BOOLEAN数据类型。

否则,最好的方法是使用NUMBER(1)或等价物,并将限制有效值为(0,1)的列设置检查约束,如果需要的话可能为NULL。如果没有内置类型,则使用数字不会含糊不清使用字符列。 (真值是什么?“T”或“Y”或“t”)

这样做的好处是可以使用SUM()来计算TRUE行的数量。

SELECT COUNT(1), SUM(ActiveFlag) 
FROM myusers; 
3

一个非常关系方法

对于数据库没有设置类型,你可以打开一个新的表来表示一组为每个标志设置实体。

E.g.对于表“学生”你可以有表格“RegisteredStudents”,“SickStudents”,麻烦学生等。每个表将只有一列:student_id。如果你想知道哪些学生是“注册”或“生病”,并且在每个DBMS中都以相同的方式工作,这实际上会非常快。