Предположим, у меня есть таблица предметов:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Теперь я хочу представить концепцию «разрешений» для каждого элемента (обратите внимание, я здесь говорю не о разрешениях доступа к базе данных, а о разрешениях бизнес-логики для этого элемента). Каждый элемент имеет разрешения по умолчанию, а также разрешения для каждого пользователя, которые могут переопределять разрешения по умолчанию.
Я попытался придумать несколько способов реализовать это и придумал следующие решения, но я не уверен, какой из них лучший и почему:
1) Булево решение
Используйте логический столбец для каждого разрешения:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Преимущества : Каждое разрешение названо.
Недостатки : существуют десятки разрешений, которые значительно увеличивают количество столбцов, и вам нужно определить их дважды (один раз в каждой таблице).
2) Целочисленное решение
Используйте целое число и обрабатывайте его как битовое поле (т. Е. Бит 0 для can_change_description
, бит 1 для can_change_price
и т. Д. И используйте побитовые операции для установки или чтения разрешений).
CREATE DOMAIN permissions AS integer;
Преимущества : очень быстро.
Недостатки : необходимо отслеживать, какой бит обозначает какое разрешение, как в базе данных, так и во внешнем интерфейсе.
3) Решение битового поля
То же, что 2), но использовать bit(n)
. Скорее всего, те же преимущества и недостатки, возможно, немного медленнее.
4) Решение Enum
Используйте тип enum для разрешений:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
а затем создайте дополнительную таблицу для разрешений по умолчанию:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
и измените таблицу определения для пользователя на:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Преимущества : легко назвать индивидуальные разрешения (вам не нужно обрабатывать битовые позиции).
Недостатки : даже при простом получении разрешений по умолчанию требуется доступ к двум дополнительным таблицам: во-первых, к таблице разрешений по умолчанию и, во-вторых, к системному каталогу, в котором хранятся значения перечисления.
Тем более, что разрешения по умолчанию должны быть получены для каждого отдельного просмотра страницы этого элемента , влияние последней альтернативы на производительность может быть значительным.
5) Решение Enum Array
То же, что 4), но использовать массив для хранения всех (по умолчанию) разрешений:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Преимущества : легко назвать индивидуальные разрешения (вам не нужно обрабатывать битовые позиции).
Недостатки : ломает 1-ую нормальную форму и немного уродлив. Занимает значительное количество байтов подряд, если количество разрешений велико (около 50).
Можете ли вы придумать другие альтернативы?
Какой подход следует использовать и почему?
Обратите внимание: это измененная версия вопроса, опубликованного ранее в Stackoverflow .
bigint
полей (каждое подходит для 64 бит) или битовую строку. Я написал пару связанных ответов на SO, которые могут помочь.