考虑以下:
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 }
不同类型的用户(直接登录用户和OpenID用户)显示IS-A关系;即,两种类型的用户都是用户.现在,有几种方法可以在RDBMS中表示:
方式一
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) )
方式二
CREATE TABLE Users ( uid INTEGER AUTO_INCREMENT NOT NULL,privilegeLevel INTEGER 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 )
方式三
CREATE TABLE DirectLoginUsers ( uid INTEGER AUTO_INCREMENT NOT NULL,PRIMARY_KEY(uid) ) CREATE TABLE OpenIDUsers ( uid INTEGER AUTO_INCREMENT NOT NULL,//OpenID Attributes PRIMARY_KEY(uid) )
解决方法
方式二是正确的方法.
您的基类获取一个表,然后子类只使用它们引入的其他字段获得自己的表,以及对基表的外键引用.
正如Joel在对此答案的评论中所建议的那样,您可以保证用户将具有直接登录或OpenID登录,但不能同时(也可能两者都没有)通过向每个子类型表添加类型列来锁定到根表.每个子类型表中的type列被限制为具有表示该表类型的单个值.由于此列是外键到根表,因此一次只能有一个子类型行链接到同一根行.
例如,MysqL DDL看起来像:
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 // ...,FORIGEN_KEY (uid,type) REFERENCES Users (uid,type) ); CREATE TABLE OpenIDLogins ( uid INTEGER NOT_NULL,type ENUM("OpenID") NOT NULL // ... PRIMARY_KEY(uid),type) );
(在其他平台上,您将使用CHECK约束而不是ENUM.)MysqL supports复合外键,因此这应该适合您.
第一种方法是有效的,尽管你在那些可以使用NULL的列中浪费空间,因为它们的使用取决于用户的类型.优点是,如果您选择扩展要存储的类型的用户类型,并且这些类型不需要其他列,则只需展开ENUM的域并使用相同的表.