Supongamos que tengo una tabla de elementos:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Ahora, quiero presentar el concepto de "permisos" para cada elemento (tenga en cuenta que no estoy hablando de permisos de acceso a la base de datos aquí, sino de permisos de lógica de negocios para ese elemento). Cada elemento tiene permisos predeterminados y también permisos por usuario que pueden anular los permisos predeterminados.
Traté de pensar en varias formas de implementar esto y se me ocurrieron las siguientes soluciones, pero no estoy seguro de cuál es la mejor y por qué:
1) La solución booleana
Use una columna booleana para cada permiso:
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,
...
);
Ventajas : se nombra cada permiso.
Desventajas : hay docenas de permisos que aumentan significativamente el número de columnas y debe definirlas dos veces (una en cada tabla).
2) La solución entera
Use un número entero y trátelo como un campo de bits (es decir, el bit 0 es para can_change_description
, el bit 1 es para can_change_price
, y así sucesivamente, y use operaciones bit a bit para establecer o leer permisos).
CREATE DOMAIN permissions AS integer;
Ventajas : muy rápido.
Desventajas : debe realizar un seguimiento de qué bit representa qué permiso tanto en la base de datos como en la interfaz front-end.
3) La solución de Bitfield
Igual que 2), pero uso bit(n)
. Lo más probable es que tengan las mismas ventajas y desventajas, quizás un poco más lento.
4) La solución Enum
Use un tipo de enumeración para los permisos:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
y luego cree una tabla adicional para los permisos predeterminados:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
y cambie la tabla de definición por usuario a:
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)
);
Ventajas : Fácil de nombrar permisos individuales (no tiene que manejar posiciones de bits).
Desventajas : incluso cuando solo se recuperan los permisos predeterminados, se requiere acceder a dos tablas adicionales: primero, la tabla de permisos predeterminada y, en segundo lugar, el catálogo del sistema que almacena los valores de enumeración.
Especialmente porque los permisos predeterminados deben recuperarse para cada vista de página de ese elemento , el impacto en el rendimiento de la última alternativa podría ser significativo.
5) La solución de matriz de enumeración
Igual que 4), pero use una matriz para mantener todos los permisos (predeterminados):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Ventajas : Fácil de nombrar permisos individuales (no tiene que manejar posiciones de bits).
Desventajas : rompe la primera forma normal y es un poco feo. Ocupa una cantidad considerable de bytes seguidos si la cantidad de permisos es grande (aproximadamente 50).
¿Se te ocurren otras alternativas?
¿Qué enfoque se debe tomar y por qué?
Tenga en cuenta: esta es una versión modificada de una pregunta publicada anteriormente en Stackoverflow .
bigint
campos (cada uno válido para 64 bits) o una cadena de bits. Escribí un par de respuestas relacionadas sobre SO que podrían ser de ayuda.