Je le demande par curiosité. Fondamentalement, ma question est quand vous avez une base de données qui a besoin d'une entrée de ligne pour avoir des choses qui agissent comme des indicateurs, quelle est la meilleure pratique? Un bon exemple de cela serait les badges sur débordement de pile ou le champ du système d'exploitation dans bugzilla. Tout sous-ensemble de drapeaux peut être défini pour une entrée donnée.
Habituellement, je travaille en c et c ++, donc ma réaction instinctive est d'utiliser un champ entier non signé comme un ensemble de bits qui peuvent être inversés ... Mais je sais que ce n'est pas une bonne solution pour plusieurs raisons. Le plus évident est la capacité de mise à l'échelle, il y aura une limite supérieure stricte sur le nombre de drapeaux que je peux avoir.
Je peux également penser à quelques autres solutions qui évoluent mieux mais qui auraient des problèmes de performances car elles nécessiteraient plusieurs sélections pour obtenir toutes les informations.
Alors, quelle est la "bonne" façon de procéder?
Si vous avez vraiment besoin d'une sélection illimitée à partir d'un ensemble fermé de drapeaux (par exemple les badges stackoverflow), la "manière relationnelle" serait de créer une table de drapeaux et une table distincte qui relie ces drapeaux à vos entités cibles. Ainsi, utilisateurs, drapeaux et usersToFlags.
Cependant, si l'efficacité de l'espace est une préoccupation sérieuse et que la capacité de requête ne l'est pas, un masque non signé fonctionnerait presque aussi bien.
D'une manière générale, j'évite les champs de masques de bits. Ils sont difficiles à lire à l'avenir et nécessitent une connaissance beaucoup plus approfondie des données pour être compris.
La solution relationnelle a été proposée précédemment. Étant donné l'exemple que vous avez décrit, je créerais quelque chose comme ceci (dans 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)
);
Dans de nombreux cas, cela dépend de beaucoup de choses - comme le backend de votre base de données. Si vous utilisez MySQL, par exemple, le SET datatype est exactement ce que vous voulez.
Fondamentalement, ce n'est qu'un masque de bits, avec des valeurs affectées à chaque bit. MySQL prend en charge des valeurs jusqu'à 64 bits (ce qui signifie 64 bascules différentes). Si vous n'avez besoin que de 8, cela ne prend qu'un octet par ligne, ce qui représente des économies considérables.
Si vous avez honnêtement plus de 64 valeurs dans un seul champ, votre champ peut devenir plus compliqué. Vous pouvez ensuite développer le type de données BLOB, qui n'est qu'un ensemble brut de bits que MySQL n'a pas de compréhension inhérente. En utilisant cela, vous pouvez créer un nombre arbitraire de champs de bits que MySQL est heureux de traiter comme des valeurs binaires, hexadécimales ou décimales, selon vos besoins. Si vous avez besoin de plus de 64 options, créez autant de champs que nécessaire pour votre application. L'inconvénient est qu'il est difficile de rendre le champ lisible par l'homme. Le type de données BIT est également limité à 64.
ne approche très relationnelle
Pour les bases de données sans le type d'ensemble, vous pouvez ouvrir une nouvelle table pour représenter l'ensemble d'entités pour lesquelles chaque indicateur est défini.
Par exemple. pour une table "Etudiants", vous pouvez avoir des tables "RegisteredStudents", "SickStudents", TroublesomeStudents etc. Chaque table n'aura qu'une seule colonne: student_id. Ce serait en fait très rapide si tout ce que vous voulez savoir, c'est quels étudiants sont "inscrits" ou "malades", et cela fonctionnerait de la même manière dans chaque SGBD.
Je recommanderais d'utiliser un type de données BOOLEAN si votre base de données le prend en charge.
Sinon, la meilleure approche consiste à utiliser NUMBER (1) ou équivalent, et à mettre une contrainte de vérification sur la colonne qui limite les valeurs valides à (0,1) et peut-être NULL si vous en avez besoin. S'il n'y a pas de type intégré, l'utilisation d'un nombre est moins ambiguë que l'utilisation d'une colonne de caractères. (Quelle est la valeur de true? "T" ou "Y" ou "t")
La bonne chose à ce sujet est que vous pouvez utiliser SUM () pour compter le nombre de vraies lignes.
SELECT COUNT(1), SUM(ActiveFlag)
FROM myusers;
Si les drapeaux ont des significations très différentes et sont utilisés directement dans les requêtes SQL ou VIEWS, alors en utilisant plusieurs colonnes de type BOOLEAN
pourrait être une bonne idée.
Mettez chaque indicateur dans une colonne supplémentaire, car vous les lirez et les modifierez de toute façon séparément. Si vous souhaitez regrouper les drapeaux, donnez simplement à leurs noms de colonnes un préfixe commun, c'est-à-dire au lieu de:
CREATE TABLE ... (
warnings INTEGER,
errors INTEGER,
...
)
tu devrais utiliser:
CREATE TABLE ... (
warning_foo BOOLEAN,
warning_bar BOOLEAN,
warning_...
error_foo BOOLEAN,
error_bar BOOLEAN,
error_... BOOLEAN,
...
)
Bien que MySQL n'ait pas de type BOOLEAN, vous pouvez utiliser le TINYINT (1) quasi standard à cet effet et le définir uniquement sur 0 ou 1.
S'il y a plus que quelques indicateurs, ou que ce sera probablement le cas à l'avenir, j'utiliserai une table distincte d'indicateurs et une table plusieurs-à-plusieurs entre eux.
S'il y a une poignée de drapeaux et que je ne les utiliserai jamais dans un WHERE, j'utiliserai un SET () ou un champ de bits ou autre. Ils sont faciles à lire et plus compacts, mais difficiles à interroger et parfois encore plus maux de tête avec un ORM.
S'il n'y a que quelques drapeaux - et seulement jamais aller pour être quelques drapeaux - alors je ferai juste quelques colonnes BIT/BOOLEAN/etc.