Массивы в таблицах базы данных и нормализация - PullRequest
2 голосов
/ 24 мая 2010

Разумно ли хранить массивы в столбцах таблицы? Точнее, я думаю о следующей схеме, которая, по моему мнению, нарушает нормализацию:


create table Permissions(
    GroupID int not null default(-1),
    CategoryID int not null default(-1),
    Permissions varchar(max) not null default(''),
    constraint PK_GroupCategory primary key clustered(GroupID,CategoryID)
);

и это:


create table Permissions(
    GroupID int not null default(-1),
    CategoryID int not null default(-1),
    PermissionID int not null default(-1),
    constraint PK_GroupCategory primary key clustered(GroupID,CategoryID)
);

UPD3: я представляю разрешения в виде строки с разделителями-запятыми, поскольку MSSQL является нашей основной целью развертывания.

UPD: Забыл упомянуть, что в рамках этого конкретного вопроса мы будем считать, что "выборка строк, имеющих разрешение X" не будет выполнена, вместо этого все поиски будут выполняться только с помощью GroupID и CategoryID

UPD2: я предполагаю типичный сценарий использования следующим образом:


int category_id=42;
int[] array_of_groups=new int[]{40,2,42};
if(!Permissions.Check(category_id, array_of_groups, Permission.EatAndDrink)) {
    throw new StarveToDeathException();
}

Мысли

Заранее спасибо!

Ответы [ 5 ]

1 голос
/ 24 мая 2010

Я бы предложил взять нормализованную дорогу по следующим причинам:

  • Имея таблицу, содержащую все возможные разрешения, вы получаете самодокументированные данные . Вы можете добавить описание к каждому разрешению. Это определенно превосходит сцепленные значения идентификаторов без какого-либо значения.
  • Вы получаете все преимущества ссылочной целостности и можете быть уверены, что в ваших данных нет поддельных идентификаторов разрешений.
  • Вставлять и удалять разрешения будет проще - вы добавляете или удаляете записи. С объединенной строкой вы будете обновлять столбец и удалять запись только при удалении последнего разрешения.
  • Ваш дизайн рассчитан на будущее - вы говорите, что хотите запрашивать только по CategoryID и GroupID, вы можете сделать это уже с нормализованными таблицами. Кроме того, вы также сможете, например, добавлять другие свойства в свои разрешения, запрашивать разрешения и т. Д.
  • Производительность -поэтому я думаю, что на самом деле будет быстрее получить набор результатов идентификаторов, чем анализировать строку в целые числа. Быть измеренным с фактическими данными и выполнением ...
0 голосов
/ 24 мая 2010

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

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

0 голосов
/ 24 мая 2010

Ваш второй пример, вероятно, должен быть:

constraint PK_GroupCategory primary key clustered(GroupID,CategoryID,PermissionID)

Ваш первый пример будет нарушать нормальную форму (и разбор строк может не использовать ваше время обработки), но это не означает, что это обязательно неправильно для вашего приложения. Это действительно зависит от того, как вы используете данные.

0 голосов
/ 24 мая 2010

Это умный

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

Если вы не видите, каким образом таблица с одной строкой для каждого элемента будет когда-либо полезной, я бы посоветовал инкапсулироватьв строке может быть рассмотрено.

В приведенном примере я хотел бы быть уверен, что выполнение запроса для поиска всех комбинаций групп / категорий для указанного разрешения не вызовет у меня проблемы, если мне придется написать предложение WHERE, в котором используется строкасопоставление с образцомКонечно, если мне никогда не придется выполнять такой запрос, то это спорный вопрос.

В целом, я наиболее доволен этим подходом, когда собираемые данные не имеют значения в отдельности : данные имеют смысл только тогда, когда рассматриваются как полный набор.Если есть немного больше структуры, скажем, список пар данных / значений, тогда форматирование с использованием XML или JSON может быть полезным.

0 голосов
/ 24 мая 2010

Проблема с первой реализацией заключается в том, что она на самом деле использует не массив, а объединенную строку.

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

Если бы вы использовали базу данных, которая изначально поддерживала массивы в качестве атомарного значения, такого как PostgreSQL, тогда аргумент был бы другим.

Исходя из второго требования предложенного запроса, я должен предположить, что второй лучше всего подходит, поскольку вы можете просто запросить SELECT count(*) FROM Permissions WHERE CategoryID = 42 AND GroupID IN (40, 2, 42) AND PermissionID = 2 (при условии, что EatAndDrink имеет идентификатор 2). Первая версия, однако, потребует извлечения всех разрешений для каждой группы и анализа строки, прежде чем вы сможете проверить, включает ли она запрошенное разрешение.

...