Considere o seguinte:
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
}
Os diferentes tipos de usuários (usuários de logon direto e usuários OpenID) exibem um relacionamento IS-A; a saber, que ambos os tipos de usuários são usuários. Agora, existem várias maneiras de representá-lo em um RDBMS:
Caminho Um
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)
)
Caminho Dois
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
)
Caminho Três
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)
)
Estou quase certo de que a terceira maneira é errada, porque não é possível fazer uma junção simples contra usuários em outras partes do banco de dados.
Meu exemplo do mundo real não é um exemplo de usuários com logins diferentes; Estou interessado em como modelar esse relacionamento no caso geral.
database-design
Billy ONeal
fonte
fonte
Respostas:
O caminho dois é o caminho correto.
Sua classe base obtém uma tabela e as classes filho obtêm suas próprias tabelas apenas com os campos adicionais que eles introduzem, além de referências de chave estrangeira à tabela base.
Como Joel sugeriu em seus comentários sobre esta resposta, você pode garantir que um usuário tenha um login direto ou um OpenID, mas não os dois (e possivelmente também nenhum) adicionando uma coluna de tipo a cada tabela de subtipo que retorna para a tabela raiz. A coluna de tipo em cada tabela de subtipo é restrita a ter um valor único que representa o tipo dessa tabela. Como esta coluna é com chave estrangeira para a tabela raiz, apenas uma linha de subtipo pode vincular à mesma linha raiz de cada vez.
Por exemplo, o DDL do MySQL se pareceria com:
(Em outras plataformas, você usaria uma
CHECK
restrição em vez deENUM
.) O MySQL suporta chaves estrangeiras compostas, portanto, isso deve funcionar para você.A primeira maneira é válida, embora você esteja desperdiçando espaço nessas
NULL
colunas ativáveis porque o uso delas depende do tipo de usuário. A vantagem é que, se você optar por expandir quais tipos de usuários armazenar e esses tipos não exigirem colunas adicionais, basta expandir o domínioENUM
e usar a mesma tabela.A maneira três força qualquer consulta que faça referência aos usuários a verificar nas duas tabelas. Isso também impede que você faça referência a uma única tabela de usuários via chave estrangeira.
fonte
UNION
, ou eu teria sugerido uma exibição indexada com um índice exclusivo contra oUNION ALL
deuid
partir as duas tabelas.)type
coluna a cada tabela de subtipo restrita porCHECK
restrição para ter exatamente um valor (o tipo dessa tabela). Em seguida, transformamos as chaves estrangeiras da sub-tabela da super-tabela em chaves compostas em ambosuid
etype
. Isso é engenhoso.Eles seriam nomeados
e todos têm seus usos legítimos e são suportados por algumas bibliotecas. Você tem que descobrir qual se encaixa melhor.
Ter várias tabelas levaria o gerenciamento de dados mais para o código do aplicativo, mas reduziria a quantidade de espaço não utilizado.
fonte