Флаги в строках базы данных, лучшие практики - PullRequest
35 голосов
/ 24 сентября 2008

Я прошу об этом из любопытства. По сути, мой вопрос: когда у вас есть база данных, в которой требуется запись строки, чтобы иметь вещи, которые действуют как флаги, какова лучшая практика? Хорошим примером этого могут быть значки переполнения стека или поле операционной системы в bugzilla. Любое подмножество флагов может быть установлено для данной записи.

Обычно я работаю с c и c ++, поэтому моя внутренняя реакция - использовать целое поле без знака в качестве набора битов, которые можно перевернуть ... Но я знаю, что это не очень хорошее решение по нескольким причинам. Самым очевидным из них является масштабируемость, и у меня будет жесткий верхний предел количества флагов.

Я также могу подумать о нескольких других решениях, которые лучше масштабируются, но будут иметь проблемы с производительностью, потому что для получения всей информации потребуется несколько вариантов выбора.

Итак, каков "правильный" способ сделать это?

Ответы [ 7 ]

29 голосов
/ 24 сентября 2008

Вообще говоря, я избегаю полей битовой маски. Их трудно читать в будущем, и для их понимания требуется гораздо более глубокое знание данных.

Реляционное решение было предложено ранее. Учитывая приведенный вами пример, я бы создал что-то вроде этого (в 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)
);
27 голосов
/ 24 сентября 2008

Если вам действительно нужен неограниченный выбор из закрытого набора флагов (например, значки stackoverflow), тогда «реляционным способом» будет создание таблицы флагов и отдельной таблицы, которая связывает эти флаги с вашими целевыми объектами. Таким образом, users, flags и usersToFlags.

Однако, если эффективность использования пространства является серьезной проблемой, а возможность запроса - нет, маска без знака будет работать почти так же хорошо.

5 голосов
/ 24 сентября 2008

Во многих случаях это зависит от многих вещей - например, от вашей базы данных. Например, если вы используете MySQL, тип данных SET - это именно то, что вам нужно.

По сути, это просто битовая маска со значениями, назначенными для каждого бита. MySQL поддерживает до 64-битных значений (имеется в виду 64 различных переключателя). Если вам нужно только 8, то он занимает всего один байт на строку, что является весьма неплохой экономией.

Если вы честно имеете более 64 значений в одном поле, ваше поле может стать более сложным. Возможно, вы захотите затем перейти к типу данных BLOB, который является просто необработанным набором битов, которые MySQL не имеет никакого внутреннего понимания. Используя это, вы можете создать произвольное количество битовых полей, которые MySQL с радостью воспримет как двоичные, шестнадцатеричные или десятичные значения, как вам нужно Если вам нужно более 64 параметров, создайте столько полей, сколько подходит для вашего приложения. Недостатком является то, что трудно сделать поле читаемым человеком. Тип BIT также ограничен 64.

4 голосов
/ 26 сентября 2008

Очень реляционный подход

Для баз данных без установленного типа вы можете открыть новую таблицу для представления набора сущностей, для которых установлен каждый флаг.

например. для таблицы «Студенты» у вас могут быть таблицы «RegisteredStudents», «SickStudents», TroublesomeStudents и т. д. Каждая таблица будет иметь только один столбец: student_id. На самом деле это будет очень быстро, если все, что вы хотите знать, это то, какие студенты являются «зарегистрированными» или «больными», и будут работать одинаково в каждой СУБД.

3 голосов
/ 25 сентября 2008

Я бы рекомендовал использовать тип данных BOOLEAN, если ваша база данных поддерживает это.

В противном случае наилучшим подходом является использование NUMBER (1) или его эквивалента и наложение столбца на столбец, ограничивающий допустимые значения (0,1) и, возможно, NULL, если вам это нужно. Если встроенного типа нет, использование числа менее неоднозначно, чем использование символьного столбца. (Какое значение для true? "T" или "Y" или "t")

Приятно то, что вы можете использовать SUM () для подсчета количества ИСТИННЫХ строк.

SELECT COUNT(1), SUM(ActiveFlag)
FROM myusers;
3 голосов
/ 24 сентября 2008

Если флаги имеют очень разные значения и используются непосредственно в запросах 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.

1 голос
/ 24 сентября 2008

Если есть несколько флагов, или, скорее всего, так будет в будущем, я буду использовать отдельную таблицу флагов и таблицу «многие ко многим» между ними.

Если есть несколько флагов, и я никогда не буду использовать их в ГДЕ, я буду использовать SET () или битовое поле или что-то еще. Их легко читать и они более компактны, но боль в запросах, а иногда и головная боль при использовании ORM.

Если есть только несколько флагов - и только когда будет быть несколькими флагами - тогда я просто сделаю пару столбцов BIT / BOOLEAN / etc.

...