数据库行中的标志,最佳实践

时间:2020-03-06 14:38:12  来源:igfitidea点击:

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

通常,我会进行c和c ++的工作,因此我的直觉反应是将无符号整数字段用作一组可以翻转的位...但是我知道,由于多种原因,这不是一个好的解决方案。最明显的是可伸缩性,我可以拥有多少个标志将有一个严格的上限。

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

那么,执行此操作的"正确"方法是什么?

解决方案

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

但是,如果空间效率是一个非常重要的问题,而查询能力却不是,则无符号掩码几乎同样适用。

在许多情况下,它取决于很多事情,例如数据库后端。例如,如果我们使用的是MySQL,则SET数据类型正是我们想要的。

基本上,它只是一个位掩码,将值分配给每个位。 MySQL最多支持64位值(表示64种不同的切换)。如果只需要8,则每行只需要一个字节,这是非常棒的节省。

如果老实说,单个字段中有64个以上的值,那么字段可能会变得越来越复杂。然后,我们可能需要扩展为BLOB数据类型,这只是MySQL本身不了解的原始位集合。使用此方法,我们可以创建任意数量的位字段,MySQL乐意将其视为二进制,十六进制或者十进制值,但是我们需要这样做。如果我们需要64个以上的选项,请根据应用程序创建尽可能多的字段。缺点是很难使该领域可读。 BIT数据类型也限制为64.

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

关系解决方案先前已经提出。给定我们概述的示例,我将在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)
);

如果存在多个标志,或者将来可能会存在,那么我将使用一个单独的标志表以及它们之间的多对多表。

如果有少数标志,而我永远也不会在WHERE中使用它们,那么我将使用SET()或者位域或者其他任何标志。它们易于阅读且结构紧凑,但是查询起来很痛苦,有时甚至会因ORM感到头疼。

如果只有几个标志-并且只会有几个标志-那么我将仅创建几个BIT / BOOLEAN / etc列。

如果标志的含义完全不同,并且直接在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.

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

否则,最好的方法是使用NUMBER(1)或者等效的方法,并在该列上放置一个检查约束,以将有效值限制为(0,1),如果需要,则可以将其限制为NULL。如果没有内置类型,则使用数字的含义要比使用字符列的含义少。 (true的值是什么?" T"或者" Y"或者" t")

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

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

一种非常关系的方法

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

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