Considérer ce qui suit:
entity User
{
autoincrement uid;
string(20) name;
int privilegeLevel;
}
entity DirectLoginUser
{
inherits User;
string(20) username;
string(16) passwordHash;
}
entity OpenIdUser
{
inherits User;
//Whatever attributes OpenID needs... I don't know; this is hypothetical
}
Les différents types d'utilisateurs (utilisateurs à connexion directe et utilisateurs OpenID) affichent une relation IS-A; à savoir, que les deux types d'utilisateurs sont des utilisateurs. Maintenant, il existe plusieurs façons de représenter cela dans un SGBDR:
première voie
CREATE TABLE Users
(
uid INTEGER AUTO_INCREMENT NOT NULL,
name VARCHAR(20) NOT NULL,
privlegeLevel INTEGER NOT NULL,
type ENUM("DirectLogin", "OpenID") NOT NULL,
username VARCHAR(20) NULL,
passwordHash VARCHAR(20) NULL,
//OpenID Attributes
PRIMARY_KEY(uid)
)
Deuxième voie
CREATE TABLE Users
(
uid INTEGER AUTO_INCREMENT NOT NULL,
name VARCHAR(20) NOT NULL,
privilegeLevel INTEGER NOT NULL,
type ENUM("DirectLogin", "OpenID") NOT NULL,
PRIMARY_KEY(uid)
)
CREATE TABLE DirectLogins
(
uid INTEGER NOT_NULL,
username VARCHAR(20) NOT NULL,
passwordHash VARCHAR(20) NOT NULL,
PRIMARY_KEY(uid),
FORIGEN_KEY (uid) REFERENCES Users.uid
)
CREATE TABLE OpenIDLogins
(
uid INTEGER NOT_NULL,
// ...
PRIMARY_KEY(uid),
FORIGEN_KEY (uid) REFERENCES Users.uid
)
Troisième voie
CREATE TABLE DirectLoginUsers
(
uid INTEGER AUTO_INCREMENT NOT NULL,
name VARCHAR(20) NOT NULL,
privlegeLevel INTEGER NOT NULL,
username VARCHAR(20) NOT NULL,
passwordHash VARCHAR(20) NOT NULL,
PRIMARY_KEY(uid)
)
CREATE TABLE OpenIDUsers
(
uid INTEGER AUTO_INCREMENT NOT NULL,
name VARCHAR(20) NOT NULL,
privlegeLevel INTEGER NOT NULL,
//OpenID Attributes
PRIMARY_KEY(uid)
)
Je suis presque certain que la troisième voie est la mauvaise, car il n'est pas possible de faire une simple jointure contre des utilisateurs ailleurs dans la base de données.
Mon exemple dans le monde réel n'est pas un exemple d'utilisateurs avec des connexions différentes; Je souhaite savoir comment modéliser cette relation dans le cas général.
La deuxième voie est la bonne.
Votre classe de base obtient une table, puis les classes enfants obtiennent leurs propres tables avec uniquement les champs supplémentaires qu'elles introduisent, ainsi que les références de clé étrangère à la table de base.
Comme Joel l'a suggéré dans ses commentaires sur cette réponse, vous pouvez garantir qu'un utilisateur aura soit une connexion directe ou une connexion OpenID, mais pas les deux (et peut-être aussi ni l'un ni l'autre) en ajoutant une colonne de type à chaque sous-type de table qui revient à la table racine. La colonne type de chaque table de sous-type est limitée à une seule valeur représentant le type de cette table. Étant donné que cette colonne est dotée d'une clé étrangère pour la table racine, une seule ligne de sous-type peut être liée à la même ligne racine à la fois.
Par exemple, le DDL MySQL ressemblerait à quelque chose comme:
CREATE TABLE Users
(
uid INTEGER AUTO_INCREMENT NOT NULL
, type ENUM("DirectLogin", "OpenID") NOT NULL
// ...
, PRIMARY_KEY(uid)
);
CREATE TABLE DirectLogins
(
uid INTEGER NOT_NULL
, type ENUM("DirectLogin") NOT NULL
// ...
, PRIMARY_KEY(uid)
, FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);
CREATE TABLE OpenIDLogins
(
uid INTEGER NOT_NULL
, type ENUM("OpenID") NOT NULL
// ...
PRIMARY_KEY(uid),
FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);
(Sur d'autres plates-formes, vous utiliseriez une contrainte CHECK
au lieu de ENUM
.) MySQL prend en charge les clés étrangères composites, cela devrait donc fonctionner pour vous.
La première façon est valide, bien que vous gaspilliez de l'espace dans ces colonnes NULL
able car leur utilisation dépend du type d'utilisateur. L'avantage est que si vous choisissez d'étendre les types de types d'utilisateurs à stocker et que ces types ne nécessitent pas de colonnes supplémentaires, vous pouvez simplement développer le domaine de votre ENUM
et utiliser la même table.
La troisième méthode force toutes les requêtes qui référencent les utilisateurs à comparer avec les deux tables. Cela vous empêche également de référencer une table d'utilisateurs unique via une clé étrangère.
Ils seraient nommés
et tous ont leurs utilisations légitimes et sont pris en charge par certaines bibliothèques. Vous devez trouver celle qui vous convient le mieux.
Avoir plusieurs tables rendrait la gestion des données plus importante pour votre code d'application mais réduirait la quantité d'espace inutilisé.