c# - ¿Cuándo es mejor almacenar banderas como una máscara de bits en lugar de usar una tabla asociativa?
sql-server database-design (9)
Almacene los permisos normalizados (es decir, no en una máscara de bits). Si bien obviamente no es un requisito para su escenario (especialmente si los permisos no cambian con frecuencia), hará que la consulta sea mucho más fácil y más obvia.
Estoy trabajando en una aplicación donde los usuarios tienen diferentes permisos para usar diferentes funciones (por ejemplo, leer, crear, descargar, imprimir, aprobar, etc.). La lista de permisos no se espera que cambie a menudo. Tengo un par de opciones sobre cómo almacenar estos permisos en la base de datos.
¿En qué casos sería mejor la Opción 2?
Opción 1
Usa una tabla asociativa.
User ---- UserId (PK) Name Department
Permission ---- PermissionId (PK) Name
User_Permission ---- UserId (FK) PermissionId (FK)
opcion 2
Almacene una máscara de bits para cada usuario.
User ---- UserId (PK) Name Department Permissions
[Flags]
enum Permissions {
Read = 1,
Create = 2,
Download = 4,
Print = 8,
Approve = 16
}
Espléndida pregunta!
En primer lugar, hagamos algunas suposiciones sobre "mejor".
Supongo que no te preocupa demasiado el espacio en disco: una máscara de bits es eficiente desde el punto de vista espacial, pero no estoy seguro de que importe mucho si estás usando el servidor SQL.
Supongo que te importa la velocidad. Una máscara de bits puede ser muy rápida al usar cálculos, pero no podrá usar un índice cuando consulte la máscara de bits. Esto no debería importar mucho, pero si quiere saber qué usuarios tienen acceso de creación, su consulta sería algo así como
select * from user where permsission & CREATE = TRUE
(no he tenido acceso a SQL Server hoy, en el camino). Esa consulta no podría usar un índice debido a la operación matemática, por lo que si tiene una gran cantidad de usuarios, esto sería bastante doloroso.
Supongo que te preocupas por la facilidad de mantenimiento. Desde un punto de vista de mantenimiento, la máscara de bits no es tan expresiva como el dominio del problema subyacente como el almacenamiento de permisos explícitos. Casi seguramente tendrá que sincronizar el valor de los indicadores de máscara de bits en varios componentes, incluida la base de datos. No es imposible, pero duele en la parte trasera.
Entonces, a menos que haya otra forma de evaluar "mejor", diría que la ruta de la máscara de bits no es tan buena como almacenar los permisos en una estructura de base de datos normalizada. No estoy de acuerdo con que sea "más lento porque tienes que unirte": a menos que tengas una base de datos totalmente disfuncional, no podrás medir esto (mientras que consultar sin el beneficio de un índice activo puede ser notoriamente más lento con incluso algunos miles de registros).
La única vez que se me ocurre cuándo usaría un campo de máscara de bits para almacenar permisos, es cuando realmente estás muy limitado en la cantidad de memoria física que tienes ... como en un viejo dispositivo móvil. En verdad, la cantidad de memoria que guardas no vale la pena. Incluso en millones de usuarios, el espacio en el disco duro es barato, y puede ampliar permisos, etc., mucho más fácilmente utilizando el enfoque que no es una máscara de bits (esto sobre informar quién tiene qué permisos, etc.)
Uno de los mayores problemas con los que me he encontrado es asignar permisos a los usuarios directamente en la base de datos. Sé que debes intentar usar la aplicación para administrarla y no mucho con los datos de la aplicación en general, pero a veces es solo necesario. A menos que la máscara de bits sea en realidad un campo de caracteres, y pueda ver fácilmente qué permisos tiene alguien en lugar de un número entero, intente explicar a un analista, etc. cómo otorgar acceso de escritura, etc. a alguien actualizando el campo ... y orando tu aritmética es correcta
No hay una respuesta definitiva , entonces haz lo que funcione para ti . Pero aquí está mi trampa:
Use la opción 1 si
- Esperas que los permisos crezcan a muchos
- Si necesita hacer una verificación de permiso en los procedimientos almacenados de la base de datos
- No esperas millones de usuarios para que los registros en la tabla no crezcan masivamente
Use la opción 2 si
- Los permisos se limitarán a un puñado
- Esperas millones de usuarios
Personalmente, usaría una tabla asociativa.
Un campo de máscara de bits es muy difícil de consultar y unirse.
Siempre puede asignar esto a su lista de indicadores de C # y, si se produce, emitir una refactorización de la base de datos.
Legibilidad sobre la optimización prematura;)
Recomiendo no usar una máscara de bits por las siguientes razones:
- El índice no se puede usar de manera eficiente
- La consulta es más difícil
- La legibilidad / mantenimiento se ve gravemente afectado
- El desarrollador promedio no sabe qué es una máscara de bits
- La flexibilidad se reduce (límite superior a nr de bits en un número)
Dependiendo de sus patrones de consulta, conjunto de características planificadas y distribución de datos, iría con su opción 1, o incluso algo simple como:
user_permissions(
user_id
,read
,create
,download
,print
,approve
,primary key(user_id)
);
Agregar una columna es una modificación de esquema, pero mi suposición es que agregar un privilegio "Purgar" requerirá algún código para ir junto con él, por lo que los privilegios pueden no tener que ser tan dinámicos como crees.
Si tiene una distribución de datos enferma, como el 90% de la base de usuarios no tiene un solo permiso, el siguiente modelo también funciona bien (pero se desmorona cuando se hacen escaneos más grandes (una combinación de 5 vías frente a una sola tabla completa) escanear).
user_permission_read(
user_id
,primary key(user_id)
,foreign key(user_id) references user(user_id)
)
user_permission_write(
user_id
,primary key(user_id)
,foreign key(user_id) references user(user_id)
)
user_permission_etcetera(
user_id
,primary key(user_id)
,foreign key(user_id) references user(user_id)
)
Será útil cuando no cambien en su estructura y siempre se usarán juntos. De esa forma, tienes pequeños viajes redondos al servidor. También son buenos en cuanto a rendimiento porque puede afectar todos los derechos en una sola asignación de una variable.
Personalmente no me gustan ... En alguna aplicación de rendimiento intenso, todavía se usan. Recuerdo haber implementado una IA de ajedrez usando esto porque podías evaluar una tabla en una sola comparación. Es un dolor trabajar con ella.
Siempre lo almacenaría normalizado a menos que la base de datos simplemente esté reteniendo el registro por usted, y nunca hará nada con esto además de recuperar y guardar. Un escenario para esto es si, al iniciar sesión, se busca la cadena de permisos de su usuario, y en el código del servidor se procesa y almacena en caché. En ese caso, realmente no importa demasiado que esté desnormalizado.
Si lo estás almacenando en una cadena y tratando de trabajar en él en el nivel DB, tendrás que hacer gimnasia para obtener los permisos para la página X, lo que puede ser doloroso.
Sus consultas se ejecutarán más rápido utilizando una enumeración de indicadores (máscara de bits), ya que no será necesario que incluya una combinación en la tabla asociada para dar sentido al valor.