Я спрашиваю об этом из любопытства. В основном мой вопрос заключается в том, что когда у вас есть база данных, в которой требуется запись строки, чтобы иметь вещи, которые действуют как флаги, что является лучшей практикой? Хорошим примером этого могут быть значки при переполнении стека или поле операционной системы в bugzilla. Для данной записи может быть установлено любое подмножество флагов.
Обычно я работаю с c и C++, поэтому моя внутренняя реакция - использовать беззнаковое целочисленное поле в качестве набора битов, которые можно перевернуть ... Но я знаю, что это не очень хорошее решение по нескольким причинам. Самым очевидным из них является масштабируемость. У меня будет жесткий верхний предел того, сколько флагов я могу иметь.
Я также могу подумать о нескольких других решениях, которые лучше масштабируются, но будут иметь проблемы с производительностью, потому что для получения всей информации потребуется несколько вариантов выбора.
Итак, каков «правильный» способ сделать это?


Если вам действительно нужен неограниченный выбор из закрытого набора флагов (например, значков stackoverflow), то «реляционным способом» будет создание таблицы флагов и отдельной таблицы, которая связывает эти флаги с вашими целевыми объектами. Таким образом, users, flags и usersToFlags.
Однако, если эффективность использования пространства вызывает серьезную озабоченность, а возможность запросов - нет, маска без знака будет работать почти так же.
Во многих случаях это зависит от многих вещей - например, от вашей базы данных. Например, если вы используете MySQL, УСТАНОВИТЬ тип данных - это именно то, что вам нужно.
По сути, это просто битовая маска, каждому биту которой присваиваются значения. MySQL поддерживает до 64-битных значений (то есть 64 различных переключателя). Если вам нужно всего 8, то для каждой строки требуется только байт, что является довольно большой экономией.
Если у вас действительно более 64 значений в одном поле, ваше поле может стать более сложным. Затем вы можете расширить его до типа данных BLOB, который представляет собой всего лишь необработанный набор битов, о котором MySQL не имеет внутреннего понимания. Используя это, вы можете создать произвольное количество битовых полей, которые MySQL будет обрабатывать как двоичные, шестнадцатеричные или десятичные значения, как вам нужно. Если вам нужно более 64 параметров, создайте столько полей, сколько подходит для вашего приложения. Обратной стороной является то, что трудно сделать поле понятным для человека. Тип данных BIT также ограничен 64.
Не то, что я бы сделал, но это хорошая реализация решения с битовой маской.
ссылка на SET datatype не работает. вот ссылка на документацию по SET для MySQL 8.0: dev.mysql.com/doc/refman/8.0/en/set.html
Вообще говоря, я избегаю полей битовых масок. Их будет трудно читать в будущем, и для их понимания требуется гораздо более глубокое знание данных.
Реляционное решение было предложено ранее. Учитывая приведенный вами пример, я бы создал что-то вроде этого (в 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, если вам это нужно. Если встроенного типа нет, использование числа менее неоднозначно, чем использование символьного столбца. (Какое значение имеет значение "истина"? "Т", "Y" или "t")
Приятно то, что вы можете использовать SUM () для подсчета количества ИСТИННЫХ строк.
SELECT COUNT(1), SUM(ActiveFlag)
FROM myusers;
Очень реляционный подход
Для баз данных без установленного типа вы можете открыть новую таблицу, чтобы представить набор сущностей, для которых установлен каждый флаг.
Например. для таблицы «Студенты» могут быть таблицы «Зарегистрированные студенты», «Больные студенты», «Проблемные студенты» и т. д. Каждая таблица будет иметь только один столбец: student_id. На самом деле это было бы очень быстро, если бы все, что вам нужно было знать, это то, какие студенты «зарегистрированы» или «больны», и будет работать одинаково в каждой СУБД.
Это действительно очень быстро, если вы хотите запросить SickStudents, а больных студентов очень мало. Это дополнительное соединение, если вы делаете что-то вроде гидратации объекта сущности.
Я наткнулся на это, когда размышлял о том, как лучше всего хранить флаги битовой маски (аналогично оригинальному использованию целых чисел OP) в базе данных.
Все остальные ответы являются действительными решениями, но я думаю, что стоит упомянуть, что вам, возможно, не придется мириться с ужасными проблемами с запросами, если вы решите хранить битовые маски непосредственно в базе данных.
Если вы работаете над приложением, которое использует битовые маски, и вам действительно нужно удобство хранения их в базе данных в виде одного целочисленного или байтового столбца, сделайте это. В дальнейшем вы можете написать себе небольшую утилиту, которая будет генерировать другую таблицу флагов (в любом шаблоне строк / столбцов, который вы выберете) из битовых масок в вашей основной рабочей таблице. Затем вы можете выполнять обычные SQL-запросы к этой вычисленной / производной таблице.
Таким образом, ваше приложение получает удобство только чтения / записи поля / столбца битовой маски. Но вы все равно можете использовать SQL, чтобы по-настоящему погрузиться в свои данные, если это станет необходимо позже.
Просто предупреждение о беззнаковой маске. Если вам нужно написать запросы, которые фильтруют строки, в которых установлен определенный бит, ваша производительность сильно пострадает, когда количество строк станет большим, потому что логические и / или операции в предложениях where не могут эффективно использовать индексы.