Как обработать несколько десятков флагов в базе данных - PullRequest
3 голосов
/ 09 августа 2009

Как и большинство приложений, у меня есть таблица «users», в которой описаны сущности, которые могут в нее войти. В нем есть информация, такая как их псевдоним, адрес электронной почты, соленые хеши паролей и все обычные кандидаты.

Однако, по мере того как мое приложение росло, мне нужны все новые и новые особые "флаги", которые я обычно просто вставляю в таблицу пользователей. Например, было ли передано их последнее ежемесячное электронное письмо, отклонили ли они всплывающее окно с учебником, сколько раз они нажимали кнопку «Я удивительный» и т. Д.

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

Что-то не так с сохранением всех этих флагов в таблице пользователей? Есть где-нибудь лучше их поставить? Будет ли создание других таблиц с соотношением 1: 1 с таблицей пользователей дополнительными издержками на получение данных, когда они мне понадобятся?

Кроме того, я использую Hibernate в качестве своего ORM, и я беспокоюсь, что создание нескольких дополнительных таблиц для этой информации означает, что мне также придется испачкать мой объект домена пользователя. Совет?

Ответы [ 6 ]

4 голосов
/ 09 августа 2009

Существует несколько распространенных решений:

  • EAV

    Сохранение одного флага на строку в дочерней таблице со ссылкой на строку пользователя, имя флага и значение. Недостатки: Невозможно гарантировать наличие строки для каждого флага. Необходимо определить другую таблицу поиска для имен флагов. Восстановление пользовательской записи со всеми ее флагами - очень дорогой запрос (требуется объединение для каждого флага).

  • Битовое поле

    Хранить один флаг на бит в одном длинном двоичном столбце. Используйте битовую маску в коде приложения для интерпретации флагов. Недостатки: Искусственный лимит на количество флагов. Трудно сбросить флаг, когда он устарел. Сложнее изменять значения флагов, искать конкретные значения флагов или агрегировать на основе значений флагов, не прибегая к запутанным побитовым операторам.

  • Нормализованный дизайн

    Сохранять один столбец BIT для каждого флага, все в таблице Users. Наиболее «правильный» дизайн с точки зрения теории отношений и нормализации. Недостатки: для добавления флага требуется ALTER TABLE ADD COLUMN. Кроме того, вы можете превысить число столбцов или размер строки, поддерживаемый вашей маркой СУБД.

2 голосов
/ 10 августа 2009

Интересно посмотреть, как самый дурацкий ответ из всех - единственный, который получил голос.

Вопрос не содержал достаточной информации, чтобы действительно дать разумный ответ.

С одной стороны, он не смог сказать, был ли вопрос о каком-то логическом дизайне базы данных или физическом дизайне базы данных.

Если вопрос был о логическом проектировании, то ответ довольно прост: НИКОГДА не включайте логическое значение в ваши логические планы. В реляционной модели уже есть способ представления информации «да / нет» (в силу предположения о замкнутом мире), а именно: наличие некоторого кортежа в некотором отношении.

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

EDIT

«Реляционная модель предписывает только один такой тип, BOOLEAN (самый фундаментальный тип из всех)». - C.J. Date, SQL и реляционная теория (2009). "

Этот ответ, конечно, должен был появиться.

Но действительно ли в этой цитате сказано, что тип boolean должен быть доступен ДЛЯ ВКЛЮЧЕНИЯ В НЕКОТОРЫЙ ТИП ОТНОШЕНИЯ? Или эта цитата (или, что лучше, более крупный фрагмент текста, в котором она появляется) лишь говорит о том, что существование типа boolean неизбежно, потому что в противном случае система не может вернуть результат для любого вызова оператора равенства, и что она действительно ли существует оператор равенства, который «прописан»?)

IOW, должен ли тип boolean быть доступным для включения в типы отношений или должен быть доступен тип boolean, потому что в противном случае не было бы ни одного языка DML, который мы могли бы определить для работы с базой данных?

Дата также официально зафиксирована, говоря (слегка перефразируя), что «если есть N способов представления информации с N> 1, то есть также> 1 набор операторов для изучения и> 1 способ для разработчика ошибки », и> 1 набор операторов для реализации разработчиком СУБД, и> 1 способ для разработчика СУБД».

РЕДАКТИРОВАТЬ РЕДАКТИРОВАТЬ

«Дата говорит, что« реляционный атрибут может быть любого типа ». Он не говорит, что атрибут может быть любого типа, кроме логического» * ​​1025 *

Вы очень хорошо прочитали Дату.

Еще одна вещь, о которой определенно не говорит Дата, - это то, что атрибут не может быть типизирован по отношению. Наоборот. Тем не менее, существует широкий консенсус, и я точно знаю, что даже Дата разделяет этот консенсус, что, вероятно, плохая идея иметь базовые relvars, которые содержат атрибут, типизированный по отношению к типу.

Аналогично, нигде Date не говорит, что это ХОРОШАЯ идея включать логические атрибуты в базовые типы отношений. Он абсолютно молчит по этому конкретному вопросу. Мнение, которое я выразил, было моим. Не думаю, что у меня сложилось впечатление, что я выражал чужое мнение в том, что написал изначально.

Представление «истинности (или ошибочности) любого данного предложения» может быть сделано путем включения / пропуска кортежа в значении отношения определенного relvar (по крайней мере, логически!). Теперь возможность включать / исключать некоторый данный кортеж из значения некоторого данного relvar, безусловно, является фундаментальной. Учитывая это, нет никакой необходимости представлять правду (или ложность) любого данного предложения (логически!) С помощью атрибута типа boolean. И для чего еще вы бы использовали атрибут типа boolean, но чтобы явно сказать, что какое-то пропущение является истинным или ложным?

2 голосов
/ 09 августа 2009

Я бы сказал, что лучший дизайн был примерно таким:

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'

и так далее. Я бы создал представление для доступа к объединенным таблицам.

1 голос
/ 09 августа 2009

Некоторым людям не нравится этот шаблон по ряду причин, но я разработал метод для двоичного сравнения строк 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 ...
}
1 голос
/ 09 августа 2009

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

Подсчет удивительных кликов также может быть выполнен путем добавления записи для каждого клика (это решает проблему обновления счетчика в записи в пользовательской таблице):

select count(*) from AwesomeClicks where userid = 1234

Используйте уникальное ограничение на поле userid для информации только в битах (действительные флаги в отличие от счетчика в приведенном выше примере).

select userid from DismissedTutorialPopup where userid = 1234

Это приведет либо к 1234 (флаг установлен), либо к нулю (флаг не установлен).

Кроме того, добавив поле CreateDate, вы можете сохранить установленный флаг и т. Д.

0 голосов
/ 09 августа 2009

**** Что-то не так с сохранением всех этих флагов в таблице пользователей? ****

Привет, я не уверен, какой Db вы используете в настоящее время, но если вы используете сервер SQLубедитесь, что размер строки не превышает 8060 байт.(максимальный размер строки 8060).

Максимальный размер строки

SQLserver 2005 - 8060 байтов MYSQL - 8052 байта Oracle 9i - 255000 байтов.

...