Припустимо, у мене є таблиця предметів:
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 для дозволів:
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
Те саме, що і 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 біт) або біт-рядок. Я написав пару пов'язаних відповідей на ТА, які можуть бути корисними.