37

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

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

我还可以想到一些其他解决方案,它们可以更好地扩展,但会出现性能问题,因为它们需要多次选择才能获取所有信息。

那么,这样做的“正确”方法是什么?

4

8 回答 8

33

一般来说,我避免使用位掩码字段。它们将来很难阅读,并且需要对数据有更深入的了解才能理解。

之前已经提出了关系解决方案。鉴于您概述的示例,我将创建类似这样的内容(在 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)
);
于 2008-09-24T01:23:45.483 回答
29

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

但是,如果空间效率是一个严重的问题而查询能力不是,那么无符号掩码几乎也可以工作。

于 2008-09-24T01:19:03.000 回答
5

在很多情况下,这取决于很多事情——比如你的数据库后端。例如,如果您使用的是 MySQL,那么SET 数据类型正是您想要的。

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

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

于 2008-09-24T01:19:17.413 回答
4

如果标志具有非常不同的含义并且直接在 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。

于 2008-09-24T17:57:57.437 回答
4

一种非常相关的方法

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

例如,对于“Students”表,您可以有“RegisteredStudents”、“SickStudents”、TroublesomeStudents 等表。每个表只有一列:student_id。如果您只想知道哪些学生是“已注册”或“生病”,这实际上会非常快,并且在每个 DBMS 中的工作方式都相同。

于 2008-09-26T12:21:57.373 回答
3

如果您的数据库支持,我建议使用 BOOLEAN 数据类型。

否则,最好的方法是使用 NUMBER(1) 或等效项,并在列上放置一个检查约束,将有效值限制为 (0,1),如果需要,可能为 NULL。如果没有内置类型,则使用数字比使用字符列更容易产生歧义。(true 的值是多少?“T”或“Y”或“t”)

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

SELECT COUNT(1), SUM(ActiveFlag)
FROM myusers;
于 2008-09-25T05:40:49.447 回答
2

如果标志不止几个,或者将来可能会出现,我将使用单独的标志表和它们之间的多对多表。

如果有一些标志并且我永远不会在 WHERE 中使用它们,我将使用 SET() 或位域或其他任何东西。它们易于阅读且更紧凑,但查询起来很痛苦,有时甚至更令人头疼的是 ORM。

如果只有几个标志——而且永远只有几个标志——那么将只创建几个 BIT/BOOLEAN/etc 列。

于 2008-09-24T01:39:21.157 回答
1

当我考虑在数据库中存储位掩码标志(类似于 OP 最初使用整数)的最佳方法时,遇到了这个问题。

其他答案都是有效的解决方案,但我认为值得一提的是,如果您选择将位掩码直接存储在数据库中,您可能不必屈服于可怕的查询问题。

如果您正在开发使用位掩码的应用程序,并且您确实希望将它们作为一个整数或字节列存储在数据库中,那么请继续这样做。以后,您可以自己编写一个小实用程序,该实用程序将从您的主工作表中的位掩码生成另一个标志表(以您选择的任何行/列模式)。然后,您可以对该计算/派生表执行普通 SQL 查询。

这样,您的应用程序就可以方便地仅读取/写入位掩码字段/列。但是,如果以后有必要,您仍然可以使用 SQL 真正深入研究您的数据。

于 2020-07-01T00:28:42.923 回答