我出于好奇而问这个问题。基本上我的问题是当你有一个数据库需要一个行条目来做一些像旗帜一样的东西时,最佳做法是什么?一个很好的例子就是堆栈溢出上的徽章,或者bugzilla中的操作系统字段。可以为给定条目设置标志的任何子集。
通常,我做c和c ++工作,所以我的直觉反应是使用无符号整数字段作为一组可以翻转的位...但我知道这不是一个好的解决方案,原因有几个。其中最明显的是比例能力,我可以拥有多少个标志的硬上限。
我还可以考虑其他几种解决方案,这些解决方案可以更好地扩展但会出现性能问题,因为它们需要多次选择才能获得所有信息。
那么,做到这一点的“正确”方法是什么?
答案 0 :(得分:29)
一般来说,我避免使用位掩码字段。它们将来很难阅读,需要更深入的数据知识才能理解。
先前已提出关系解决方案。根据您概述的示例,我将创建类似这样的内容(在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 :(得分:27)
如果你真的需要一组封闭的标志(例如stackoverflow标志)中的无界选择,那么“关系方式”将是创建一个标志表和一个单独的表,它将这些标志与你的目标实体联系起来。因此,用户,标志和usersToFlags。
但是,如果空间效率是一个严重问题而且查询能力不是,那么无符号掩码几乎也可以。
答案 2 :(得分:5)
在许多情况下,它取决于很多东西 - 比如你的数据库后端。例如,如果你正在使用MySQL,那么SET datatype就是你想要的。
基本上,它只是一个位掩码,每个位都分配了值。 MySQL支持最多64位值(意味着64个不同的切换)。如果你只需要8,那么每行只需要一个字节,这是相当可观的节省。
如果你真的在一个字段中拥有超过64个值,那么你的字段可能会变得更加复杂。您可能希望扩展到BLOB数据类型,这只是MySQL没有固有理解的原始位集。使用它,您可以创建任意数量的位字段,MySQL很乐意将其视为二进制,十六进制或十进制值,但您需要。如果您需要超过64个选项,请创建适合您的应用程序的多个字段。缺点是难以使人类可读的领域。 BIT datatype也限制为64。
答案 3 :(得分:4)
一种非常关系的方法
对于没有设置类型的数据库,您可以打开一个新表来表示为其设置每个标志的实体集。
E.g。对于表“学生”,你可以有“RegisteredStudents”,“SickStudents”,TroublesomeStudents等表。每个表只有一列:student_id。如果您只想知道哪些学生是“已注册”或“生病”,这实际上会非常快,并且在每个DBMS中都会以相同的方式工作。
答案 4 :(得分:3)
如果标志具有非常不同的含义并且直接在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没有BOOLEAN类型,但您可以将准标准TINYINT(1)用于此目的,并将其设置为0或1。
答案 5 :(得分:3)
如果您的数据库支持,我建议使用BOOLEAN数据类型。
否则,最好的方法是使用NUMBER(1)或等效,并在限制有效值为(0,1)的列上放置检查约束,如果需要,可能为NULL。如果没有内置类型,使用数字比使用字符列更不明确。 (什么是真的值?“T”或“Y”或“t”)
关于这一点的好处是你可以使用SUM()来计算TRUE行的数量。
SELECT COUNT(1), SUM(ActiveFlag)
FROM myusers;
答案 6 :(得分:1)
如果将来不仅仅有几个标志,或者可能会有这样的标志,我将使用一个单独的标志表和它们之间的多对多表。
如果有一些标志并且我永远不会在WHERE中使用它们,我将使用SET()或位域或其他。它们易于阅读且更紧凑,但查询很痛苦,有时甚至更令ORM头疼。
如果只有几个标志 - 并且只有将作为一些标志 - 那么我只会制作一对BIT / BOOLEAN / etc列。
答案 7 :(得分:0)
当我在考虑在数据库中存储位掩码标志(类似于OP最初对整数的使用)的最佳方法时,遇到这种情况。
其他答案都是有效的解决方案,但是我认为值得一提的是,如果您选择直接在数据库中存储位掩码,则可能不必让自己陷入可怕的查询问题。
如果您正在使用使用位掩码的应用程序,并且确实希望方便地将它们作为一个整数或字节列存储在数据库中,请继续进行操作。在路上,您可以编写一个实用程序,它将从主工作表的位掩码中生成另一个标志表(以您选择的任何行/列模式)。然后,您可以在该计算/派生表上执行普通SQL查询。
这样,您的应用程序将获得仅读取/写入位掩码字段/列的便利。但是,如果以后有必要,您仍然可以使用SQL真正深入数据。