Supponiamo di avere una tabella di articoli:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Ora, voglio introdurre il concetto di "autorizzazioni" per ciascun elemento (si noti che non sto parlando di autorizzazioni di accesso al database qui, ma autorizzazioni di business logic per quell'elemento). Ogni elemento ha autorizzazioni predefinite e anche autorizzazioni per utente che possono sovrascrivere le autorizzazioni predefinite.
Ho provato a pensare a diversi modi per implementarlo e ho trovato le seguenti soluzioni, ma non sono sicuro su quale sia il migliore e perché:
1) La soluzione booleana
Utilizzare una colonna booleana per ogni autorizzazione:
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,
...
);
Vantaggi : ogni autorizzazione è denominata.
Svantaggi : ci sono dozzine di autorizzazioni che aumentano significativamente il numero di colonne e devi definirle due volte (una volta in ogni tabella).
2) La soluzione integer
Utilizzare un numero intero e trattarlo come un campo di bit (ovvero il bit 0 è per can_change_description
, il bit 1 è per can_change_price
e così via e utilizzare le operazioni bit a bit per impostare o leggere le autorizzazioni).
CREATE DOMAIN permissions AS integer;
Vantaggi : molto veloce.
Svantaggi : è necessario tenere traccia di quale bit rappresenta l'autorizzazione sia nel database che nell'interfaccia front-end.
3) La soluzione Bitfield
Come 2), ma usa bit(n)
. Molto probabilmente gli stessi vantaggi e svantaggi, forse leggermente più lenti.
4) La soluzione Enum
Utilizzare un tipo enum per le autorizzazioni:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
e quindi creare una tabella aggiuntiva per le autorizzazioni predefinite:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
e modifica la tabella delle definizioni per utente in:
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)
);
Vantaggi : facile nominare le singole autorizzazioni (non è necessario gestire le posizioni dei bit).
Svantaggi : anche quando si recuperano solo le autorizzazioni predefinite, è necessario accedere a due tabelle aggiuntive: prima, la tabella delle autorizzazioni predefinite e, in secondo luogo, il catalogo di sistema che memorizza i valori enum.
Soprattutto perché le autorizzazioni predefinite devono essere recuperate per ogni singola visualizzazione di pagina di quell'elemento , l'impatto sulle prestazioni dell'ultima alternativa potrebbe essere significativo.
5) La soluzione di matrice Enum
Come 4), ma usa un array per contenere tutte le autorizzazioni (predefinite):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Vantaggi : facile nominare le singole autorizzazioni (non è necessario gestire le posizioni dei bit).
Svantaggi : rompe la prima forma normale ed è un po 'brutta. Occupa un numero considerevole di byte consecutivi se il numero di autorizzazioni è elevato (circa 50).
Riesci a pensare ad altre alternative?
Quale approccio dovrebbe essere adottato e perché?
Nota: questa è una versione modificata di una domanda pubblicata in precedenza su StackOverflow .
bigint
campi (ciascuno buono per 64 bit) o una stringa di bit. Ho scritto un paio di risposte correlate su SO che potrebbero essere di aiuto.