Как обработать несколько десятков флагов в базе данных
Как и большинство приложений, у меня есть таблица "пользователи", в которой описаны сущности, которые могут в нее войти. В нем есть информация, такая как их псевдоним, адрес электронной почты, соленые хеши паролей и все обычные кандидаты.
Тем не менее, по мере роста моего приложения, мне нужны были все новые и новые особые "флаги", которые я обычно просто вставлял в таблицу пользователей. Например, были ли переданы их последние ежемесячные электронные письма, отклонили ли они всплывающее окно с учебником, сколько раз они нажимали кнопку "Я удивительный" и т. Д.
Я начинаю иметь довольно много из этих полей, и большинство этих флагов мне не нужно для большинства веб-страниц, которые я обрабатываю.
Что-то не так с сохранением всех этих флагов в таблице пользователей? Есть ли где-нибудь лучше их поставить? Будет ли создание других таблиц с соотношением 1:1 с таблицей пользователей дополнительными затратами на извлечение данных, когда они мне понадобятся?
Кроме того, я использую Hibernate в качестве своего ORM, и я беспокоюсь, что создание нескольких дополнительных таблиц для этой информации означает, что мне также придется испачкать мой объект домена пользователя. Совет?
6 ответов
Есть несколько общих решений:
EAV
Сохраните один флаг на строку в дочерней таблице со ссылкой на строку пользователя, имя флага и значение. Недостатки: Невозможно гарантировать наличие строки для каждого флага. Необходимо определить другую таблицу поиска для имен флагов. Воссоздание пользовательской записи со всеми ее флагами - очень дорогой запрос (требуется объединение для каждого флага).
Битовое поле
Храните один флаг на бит в одном длинном двоичном столбце. Используйте битовую маску в коде приложения для интерпретации флагов. Недостатки: Искусственный лимит на количество флагов. Трудно сбросить флаг, когда он устарел. Сложнее изменять значения флагов, искать конкретные значения флагов или агрегировать на основе значений флагов, не прибегая к запутанным побитовым операторам.
Нормализованный дизайн
Магазин один
BIT
столбец за флагом, все в таблице Users. Наиболее "правильный" дизайн с точки зрения теории отношений и нормализации. Недостатки: для добавления флага требуетсяALTER TABLE ADD COLUMN
, Кроме того, вы можете превысить число столбцов или размер строки, поддерживаемый вашей маркой СУБД.
Я бы сказал, что лучший дизайн был примерно таким:
create table users (
id integer primary key,
user varchar(32) unique
)
create table flags (
id integer,
flagname varchar(32),
flagval char(1)
)
с первичным ключом id + flagname. Флаги записей тогда выглядят так:
1, 'administrator', 'Y',
1, 'editor', 'Y',
2, 'editor' 'Y'
и так далее. Я бы создал представление для доступа к объединенным таблицам.
Интересно посмотреть, как самый дурацкий ответ из всех - единственный, который получил одобрение.
Вопрос не содержал достаточной информации, чтобы действительно дать разумный ответ.
С одной стороны, он не смог сказать, был ли вопрос о каком-то логическом дизайне базы данных или физическом дизайне базы данных.
Если вопрос был о логическом дизайне, то ответ довольно прост: НИКОГДА не включайте логическое значение в ваши логические планы. У реляционной модели уже есть способ представления информации "да / нет" (в силу предположения о замкнутом мире), а именно: наличие некоторого кортежа в некотором отношении.
Если вопрос касался физического плана, то любой разумный ответ должен обязательно зависеть от другой информации, такой как частота обновления, частота запросов, объемы запрашиваемых данных и т. Д. И т. Д. Ни один из них не был предоставлен, что делает вопрос без ответа.
РЕДАКТИРОВАТЬ
"Реляционная модель предписывает только один такой тип, BOOLEAN (самый фундаментальный тип из всех)". - CJ Date, SQL и реляционная теория (2009)."
Этот ответ, конечно, должен был появиться.
Но действительно ли эта цитата говорит о том, что тип boolean должен быть доступен ДЛЯ ВКЛЮЧЕНИЯ В НЕКОТОРЫЙ ТИП ОТНОШЕНИЯ? Или эта цитата (или, точнее, более крупный фрагмент текста, в котором она появляется) лишь говорит о том, что существование типа boolean неизбежно, потому что в противном случае система не может вернуть результат для любого вызова оператора равенства, и что она действительно ли существует оператор равенства, который "прописан"?)
Итак, должен ли тип boolean быть доступным для включения в типы отношений или должен быть доступен тип boolean, потому что в противном случае не было бы единого языка DML, который мы могли бы определить для работы с базой данных?
Дата также официально зафиксирована, говоря (слегка перефразируя), что "если существует N способов представления информации с N > 1, то есть также> 1 набор операторов для изучения и> 1 способ для разработчика делать ошибки", и> 1 набор операторов для реализации разработчиком СУБД и> 1 способ для разработчика ошибок СУБД ".
РЕДАКТИРОВАТЬ РЕДАКТИРОВАТЬ
"Дата говорит, что" реляционный атрибут может быть любого типа. Он не говорит, что атрибут может быть любого типа, кроме логического "
Вы очень хорошо прочитали Дату.
Еще одна вещь, о которой определенно не говорит Дата, - это то, что атрибут не может быть типизирован по отношению. Наоборот. Тем не менее, существует широкий консенсус, и я точно знаю, что даже Дата разделяет этот консенсус, что, вероятно, плохая идея иметь базовые relvars, которые включают атрибут, типизированный по отношению.
Аналогично, нигде Date не говорит, что это ХОРОШАЯ идея включить логические атрибуты в базовые типы отношений. Он абсолютно молчит по этому конкретному вопросу. Мнение, которое я выразил, было моим. Я не думаю, что у меня сложилось впечатление, что я выражал чужое мнение в том, что я написал изначально.
Представление "истинности (или ложности) любого данного предложения" может быть сделано путем включения / пропуска кортежа в значении отношения определенного relvar (по крайней мере, логически!). Теперь возможность включать / исключать некоторый данный кортеж из значения некоторого данного relvar, безусловно, является фундаментальной. Учитывая это, нет никакой необходимости представлять правду (или ложность) любого данного предложения (логически!) С помощью атрибута типа boolean. И для чего еще вы бы использовали атрибут типа boolean, но чтобы явно сказать, что какое-то пропущение является истинным или ложным?
Если вам действительно нужна эта информация только на нескольких страницах, почему бы не иметь таблицу и отношение для каждого флага? Наличие записи в этой таблице устанавливает бит, выбирая null
это ненастроенный бит.
Подсчет удивительных кликов также может быть выполнен путем добавления записи для каждого клика (это решает проблему обновления счетчика в записи в пользовательской таблице):
select count(*) from AwesomeClicks where userid = 1234
Используйте уникальное ограничение на userid
поле для информации только для бита (действительные флаги в отличие от счетчика в приведенном выше примере).
select userid from DismissedTutorialPopup where userid = 1234
Это приведет либо к 1234 (флаг установлен), либо к нулю (флаг не установлен).
Кроме того, добавив CreateDate
поле, вы можете сохранить, когда флаг был установлен и т. д.
Некоторым людям не нравится этот шаблон по ряду причин, но я разработал метод для двоичного сравнения строк base64, чтобы я мог обрабатывать практически неограниченное количество флагов внутри одного поля varchar. (6 на персонажа технически)
Я допускаю одно разочарование в связи с этой техникой, что почти невозможно прочитать их из базы данных. Но это работает для меня. Мои флаги определены в моем приложении так:
public class Flags
{
public const string Flag1 = "1";
public const string Flag2 = "2";
public const string Flag3 = "4";
public const string Flag4 = "8";
public const string Flag5 = "g";
public const string Flag6 = "w";
public const string Flag7 = "10";
// ... etc ...
}
**** Что-нибудь не так с сохранением всех этих флагов в таблице пользователей?****
Привет, я не уверен, какой Db вы используете в настоящее время, но если вы используете сервер SQL, убедитесь, что размер строки не будет 8060 байт. (максимальный размер строки 8060).
МАКСИМАЛЬНЫЙ размер строки
SQLserver 2005 - 8060 байт MYSQL - 8052 байта, Oracle 9i - 255000 байт.