Modelando uma estrutura de banco de dados para vários tipos de usuários e suas informações de contato


10

Estou projetando um banco de dados que armazenará usuários de diferentes tipos. Predominantemente (mas não exclusivamente) eles serão Atores, Diretores e Escritores. Atualmente, existem apenas quatro tipos de usuários relevantes. Há uma chance externa de que esse número possa aumentar, mas a probabilidade é baixa - e, nesse caso, seria por um número muito pequeno.

O plano é ter uma userstabela responsável basicamente pelo login no site ( name, emaile passwordcolunas, mais uma ou duas outras, como se foram aprovadas e atualizadas) e tabelas adicionais para cada um dos respectivos tipos de usuário que cada têm seu próprio conjunto exclusivo de colunas. Apenas atores, por exemplo, terão uma coluna de etnia, apenas diretores terão uma coluna de biografia e apenas escritores precisarão fornecer sua localização. No entanto, como eu não gerenciei um banco de dados dessa complexidade antes, estou pensando em como organizar alguns aspectos:

Em primeiro lugar, os usuários podem ser qualquer um, ou qualquer combinação, dos tipos acima. Então eu entendo que precisaria de algo como (por exemplo) uma director_usertabela com director_ide user_idcolunas. Isso seria suficiente para filtrar todos os usuários por tipo de função e assim por diante?

Em segundo lugar, a maioria dos usuários terá a opção de um perfil e número de telefone no twitter. E todos os atores deverão incluir pelo menos um URL para qualquer um dos outros perfis de atores on-line; Atualmente, existem três que eles podem incluir, mas esse número pode aumentar. Estou certo ao supor que uma tabela separada para cada um dos possíveis perfis / métodos de contato seja uma maneira ideal de organizar dados?

Respostas:


14

De acordo com minha interpretação de sua descrição do contexto de negócios de interesse, você está lidando com uma estrutura de supertipo-subtipo 1 , em que (a) Ator , Diretor e Escritor são subtipos de entidade de (b) Pessoa , supertipo de entidade e (c) os referidos subtipos não são mutuamente exclusivos.

Dessa forma, se você estiver interessado em construir um banco de dados relacional que espelhe esse cenário com precisão - e, portanto, espere que ele funcione como tal -, seus esclarecimentos a seguir são bastante significativos em relação aos pontos anteriores, porque têm implicações em (1) os níveis conceitual e (2) lógico de representação do banco de dados em questão:

  • […] Tabelas adicionais para cada um dos respectivos tipos de usuário, cada um com seu próprio conjunto de colunas exclusivo.

  • […] Existem apenas quatro tipos de usuários que são relevantes. Há uma chance externa de que esse número possa aumentar, mas a probabilidade é baixa - e, nesse caso, seria por um número muito pequeno.

Vou elaborar todos esses aspectos e vários outros fatores críticos nas seções abaixo.

Regras do negócio

Para definir primeiro o esquema conceitual correspondente - que pode ser usado como referência subsequente para que você possa adaptá-lo para garantir que ele atenda aos requisitos informacionais exatos - , formulamos algumas regras de negócios que são de particular importância:

  • Uma Pessoa pode desempenhar uma ou duas ou três funções (um para todos) 2 . Em outras palavras, uma Pessoa pode ser
    • um ator e
    • um diretor e
    • um escritor .
  • Uma Pessoa pode efetuar login via UserProfile zero ou um .
  • Um ator fornece um ou dois ou três URLs 3 .
  • Um ator é agrupado por uma etnia .
  • Uma etnia agrupa zero ou um ou muitos atores .
  • Um escritor é baseado em um local .
  • Um local é a base de zero ou um ou mais escritores .

Diagrama do IDEF1X expositivo

Em seguida, criei o diagrama IDEF1X 4 mostrado na Figura 1 , que agrupa todas as formulações acima, juntamente com outras regras que parecem pertinentes:

Figura 1 - Diagrama IDEF1X para funções de pessoa e detalhes de contato no cinema

Como demonstrado, o supertipo Pessoa (i) possui sua própria caixa, (ii) possui as propriedades ou atributos que se aplicam a todos os subtipos e (iii) apresenta linhas que o conectam às caixas de cada subtipo.

Por sua vez, todo subtipo (a) aparece em sua própria caixa dedicada e (b) mantém exclusivamente suas propriedades aplicáveis. A CHAVE PRIMÁRIA do supertipo, PersonId , migra 5 para os subtipos com os nomes de função 6 ActorId , DirectorId e WriterId, respectivamente.

Além disso, evitei acoplar Person ao tipo de entidade UserProfile , que permite separar todas as implicações contextuais, associações ou relacionamentos etc. A propriedade PersonId foi migrada para UserProfile com o nome de função UserId .

Você declara no corpo da pergunta que

E todos os atores deverão incluir pelo menos um URL para qualquer um dos outros perfis de atores on-line; Atualmente, existem três que eles podem incluir, mas esse número pode aumentar.

… Portanto, o URL é um tipo de entidade por direito próprio e está diretamente associado ao subtipo de ator , de acordo com esta citação.

E, nos comentários , você especifica que

[…] Um ator terá um tiro na cabeça (foto), enquanto um escritor não terá […]

… Então, entre outros recursos, incluí o Headshot como uma propriedade do tipo de entidade ator .

Quanto aos tipos de entidade Etnia e Localização , é claro que eles podem envolver organizações mais complexas (por exemplo, um Ator pode pertencer a um, dois ou mais grupos étnicos diferentes em proporções distintas, e um Escritor pode basear-se em um local que requer gravação país, região administrativa, município etc.), mas parece que as necessidades do seu contexto de negócios são cobertas com êxito pelas estruturas aqui modeladas.

Naturalmente, você pode fazer quantos ajustes forem necessários.

Projeto lógico SQL-DDL ilustrativo

Consequentemente, com base no diagrama IDEF1X mostrado e descrito acima, escrevi o layout DDL lógico que é mostrado a seguir (forneci notas como comentários que explicam algumas das características que considero particularmente importantes em relação às tabelas, colunas e restrições declarado):

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- Also, you should make accurate tests to define the 
-- most convenient INDEX strategies based on the exact 
-- data manipulation tendencies of your business needs.

-- As one would expect, you are free to utilize 
-- your preferred (or required) naming conventions. 

CREATE TABLE Person ( -- Represents the supertype.
    PersonId       INT      NOT NULL,
    FirstName      CHAR(30) NOT NULL,
    LastName       CHAR(30) NOT NULL,
    BirthDate      DATE     NOT NULL,
    GenderCode     CHAR(3)  NOT NULL,
    TwitterProfile CHAR(30) NOT NULL,
    PhoneNumber    CHAR(30) NOT NULL,
    EmailAddress   CHAR(30) NOT NULL,  
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Person_PK  PRIMARY KEY (PersonId),
    CONSTRAINT Person_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        GenderCode,
        BirthDate
    ),
    CONSTRAINT Person_AK2 UNIQUE (TwitterProfile), -- ALTERNATE KEY.
    CONSTRAINT Person_AK3 UNIQUE (EmailAddress)    -- ALTERNATE KEY.
);

CREATE TABLE Ethnicity ( -- Its rows will serve a “look-up” purpose.
    EthnicityId     INT      NOT NULL,
    Name            CHAR(30) NOT NULL,  
    Description     CHAR(30) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT Ethnicity_PK PRIMARY KEY (EthnicityId),
    CONSTRAINT Ethnicity_AK UNIQUE      (Description)   
);

CREATE TABLE Actor ( -- Stands for one of the subtypes.
    ActorId         INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Headshot        CHAR(30) NOT NULL, -- May, e.g., contain a URL indicating the path where the photo file is actually stored. 
    EthnicityId     INT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT Actor_PK            PRIMARY KEY (ActorId),
    CONSTRAINT ActorToPerson_PK    FOREIGN KEY (ActorId)
        REFERENCES Person (PersonId),
    CONSTRAINT ActorToEthnicity_PK FOREIGN KEY (EthnicityId)
        REFERENCES Ethnicity (EthnicityId)   
);

CREATE TABLE Director ( -- Denotes one of the subtypes
    DirectorId      INT       NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    Bio             CHAR(120) NOT NULL,  
    Etcetera        CHAR(30)  NOT NULL,
    CreatedDateTime DATETIME  NOT NULL,
    -- 
    CONSTRAINT Director_PK         PRIMARY KEY (DirectorId),
    CONSTRAINT DirectorToPerson_PK FOREIGN KEY (DirectorId)
        REFERENCES Person (PersonId)   
);

CREATE TABLE Country (
    CountryCode     CHAR(2)  NOT NULL,
    Name            CHAR(30) NOT NULL,  
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT Country_PK PRIMARY KEY (CountryCode),
    CONSTRAINT Country_AK UNIQUE      (Name)   
);

CREATE TABLE Location ( -- Its rows will serve a “look-up” purpose.
    CountryCode     CHAR(2)  NOT NULL,
    LocationCode    CHAR(3)  NOT NULL,
    Name            CHAR(30) NOT NULL,  
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT Location_PK PRIMARY KEY (CountryCode, LocationCode),
    CONSTRAINT Location_AK UNIQUE      (CountryCode, Name)   
);

CREATE TABLE Writer ( -- Represents one of the subtypes.
    WriterId        INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    CountryCode     CHAR(2)  NOT NULL,
    LocationCode    CHAR(3)  NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT Writer_PK           PRIMARY KEY (WriterId),
    CONSTRAINT WriterToPerson_PK   FOREIGN KEY (WriterId)
        REFERENCES Person (PersonId),
    CONSTRAINT WriterToLocation_PK FOREIGN KEY (CountryCode, LocationCode)
        REFERENCES Location (CountryCode, LocationCode)  
);

CREATE TABLE UserProfile (
    UserId          INT      NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
    UserName        CHAR(30) NOT NULL,
    Etcetera        CHAR(30) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK UNIQUE      (UserName), -- ALTERNATE KEY.
    CONSTRAINT UserProfileToPerson_PK FOREIGN KEY (UserId)
        REFERENCES Person (PersonId)    
);

CREATE TABLE URL (
    ActorId       INT      NOT NULL,
    Address       CHAR(90) NOT NULL,
    Etcetera      CHAR(30) NOT NULL,
    AddedDateTime DATETIME NOT NULL,
    -- 
    CONSTRAINT URL_PK        PRIMARY KEY (ActorId, Address), -- Composite PRIMARY KEY.
    CONSTRAINT URLtoActor_FK FOREIGN KEY (ActorId)
        REFERENCES Actor (ActorId)
);

Portanto, (1) todo aspecto singular do layout lógico acima possui um significado muito preciso de (2) uma característica singular do ambiente de negócios de interesse 7 - de acordo com o espírito da estrutura relacional do Dr. Edgar Frank Codd -, Porque:

  • Cada tabela base representa um tipo de entidade individual.
  • Cada coluna representa uma única propriedade do respectivo tipo de entidade.
  • Um tipo de dados específico é fixado para cada coluna , a fim de garantir que todos os valores contidos pertençam a um conjunto a, específico e adequadamente delimitado, seja INT, DATETIME, CHAR, etc (e esperemos que o MySQL finalmente incorpore DOMAIN suporte em versão de futuro próximo).
  • Múltiplas restrições são configuradas (declarativamente) para garantir que as asserções em forma de linhas retidas em todas as tabelas estejam em conformidade com as regras de negócios determinadas no nível conceitual.
  • Cada linha destina-se a transmitir semânticas bem definidas, por exemplo, uma Personlinha é lida

    A Pessoa identificada por PersonId ré chamada pelo Nome se o Sobrenome t, nasceu em BirthDate u, possui o GenderCode v, os tweets no TwitterProfile w, é alcançado através do PhoneNumber x, é contatado pelo EmailAddress ye registrado no CreatedDateTimez .

Ter um layout como esse é decididamente favorável, pois você pode derivar novas tabelas (por exemplo, operações SELECT que reúnem colunas FROM várias tabelas com a ajuda da cláusula JOIN) que - em sucessão - também possuem um significado muito preciso (consulte a seção intitulado "Visualizações" abaixo).

É importante mencionar que, com essa configuração, (i) uma linha representando uma instância de subtipo é identificada por (ii) o mesmo valor PRIMARY KEY que distingue a linha que indica a ocorrência complementar do supertipo. Portanto, é mais do que oportuno observar que

  • (a) anexar uma coluna extra para conter substitutos gerados e atribuídos pelo sistema 8 a (b) as tabelas que representam os subtipos são (c) inteiramente supérfluas .

Com esse design lógico, se novos subtipos forem definidos como relevantes em seu contexto de negócios, você deverá declarar uma nova tabela base, mas isso acontecerá também quando outros tipos de tipos de entidade forem considerados significativos, portanto, a situação seria fato, comum.

Visualizações

A fim de “buscar”, por exemplo, toda a informação que corresponde a um Ator , Diretor ou escritor , você pode declarar alguns pontos de vista (isto é, derivado ou exprimíveis tabelas) de modo que você pode selecionar diretamente a partir de um único recurso sem ter que escrever o sobre JOINs sempre; por exemplo, com a VIEW declarada abaixo, você pode obter as informações “completas” do ator :

--
CREATE VIEW FullActor AS

    SELECT P.FirstName,
           P.Lastname,
           P.BirthDate,
           P.GenderCode,
           P.TwitterProfile,
           P.PhoneNumber,
           P.EmailAddress,
           A.Headshot,
           E.Name AS Ethnicity
         FROM Person P
         JOIN Actor A
           ON A.ActorId     = P.PersonId
         JOIN Ethnicity E
           ON E.EthnicityId = A.EthnicityId;
--

Obviamente, você pode seguir uma abordagem semelhante para recuperar as informações “completas” do diretor e escritor :

--
CREATE VIEW FullDirector AS

    SELECT P.FirstName,
           P.Lastname,
           P.BirthDate,
           P.GenderCode,
           P.TwitterProfile,
           P.PhoneNumber,
           P.EmailAddress,
           D.Bio,
           D.Etcetera
         FROM Person P
         JOIN Director D
           ON D.DirectorId = P.PersonId; 

--
CREATE VIEW FullWriter AS

    SELECT P.FirstName,
           P.Lastname,
           P.BirthDate,
           P.GenderCode,
           P.TwitterProfile,
           P.PhoneNumber,
           P.EmailAddress,
           L.Name AS Location,
           C.Name AS Country
         FROM Person P
         JOIN Writer W
           ON W.WriterId     = P.PersonId
         JOIN Country C
           ON C.CountryCode  = W.CountryCode
         JOIN Location L
           ON L.LocationCode = W.LocationCode;   
--

Publiquei todas as instruções DDL e as visualizações DML aqui discutidas neste SQL Fiddle em execução no MySQL 5.6 para que você possa vê-las e testá-las "em ação".


Notas finais

1 Em algumas técnicas de modelagem conceitual, as associações supertipo-subtipo são chamadas de relacionamentos superclasse-subclasse .

2 Embora você mencione que existem de fato mais funções que uma Pessoa pode desempenhar, mas as três que você revelou são boas o suficiente para discutir o cenário que expõe várias ramificações importantes .

3 Mas, como você observou, no futuro um ator poderá eventualmente fornecer URLs um para muitos .

4 Definição de Integração para Modelagem de Informações ( IDEF1X ) é uma técnica de modelagem altamente recomendável que foi estabelecida como padrão em dezembro de 1993 pelo Instituto Nacional de Padrões e Tecnologia (NIST) dos Estados Unidos . É baseado em (a) os primeiros trabalhos teóricos de autoria do único autor do modelo relacional de dados, ou seja, Dr. EF Codd; (b) a visão de relacionamento entre entidades , desenvolvida pelo Dr. PP Chen ; e também (c) a Logical Database Design Technique, criada por Robert G. Brown.

5 O padrão IDEF1X define a migração de chave como “O processo de modelagem de colocar a chave primária de uma entidade pai ou genérica [isto é, um supertipo] em sua entidade filha ou categoria [ou seja, um subtipo] como uma chave estrangeira”.

6 No IDEF1X, um nome de função é um rótulo distinto atribuído a um atributo FK para expressar o significado que ele possui no escopo de seu respectivo tipo de entidade.

7 Exceto, naturalmente, as propriedades conceituais hipotéticas (e as colunas lógicas) Director.Etcetera e UserProfile.Etcetera , que são meramente espaços reservados que eu usei para expor a possibilidade de adicionar mais propriedades (e colunas) que se aplicam ao tipo de entidade conceitual correspondente (e tabela lógica).

8 Por exemplo, anexando uma coluna adicional com o atributo AUTO_INCREMENT a uma tabela de um banco de dados "em execução" no MySQL.


2

Você deve dividir isso em tabelas como esta (mostrando apenas as colunas necessárias para apresentar o conceito, não necessariamente todas as colunas):

Users
ID   Username   FirstName   LastName   PasswordHash ...
 1   'Joe1'      'Joe'      'Smith'
 2   'Freddy'    'Fred'     'Jones'

Roles
ID   RoleType ....
 1   'Writer'
 2   'Director'
 3   'Actor'

User_Roles
User_ID   Role_ID ...
1         1
1         2
2         2
2         3

Isso fornece uma tabela cheia de usuários com todas as várias colunas de usuários, uma tabela de funções e uma tabela de vinculação para conectar as duas.

Você pode ver que Joe1 é um escritor e diretor pelas entradas em User_Roles. E Freddy é diretor e ator.

Isso também permite adicionar mais funções posteriormente, sem alterar o sistema. Basta inserir registros para o Produtor ou Editor ou o que for mais adiante.

Portanto, para encontrar todos os nomes de usuários dos atores, você tem algumas opções:

 Select Distinct Username
   from Users
  Where User_ID in (select User_ID from User_Roles where Role_ID = 3)

Ou se você não souber o número role_ID, então:

 Select Distinct Username
   from Users
  Where User_ID in (Select User_ID from User_Roles where Role_ID = 
                       (Select ID from Roles where RoleType = 'Actor')
                   )

Ou você também pode fazer isso:

select u.Username, r.RoleType
  from Users u
 inner join User_Roles ur on ur.User_ID = u.ID
 inner join Roles r on r.ID = ur.Role_ID
 where r.RoleType = 'Actor'

(Nesta versão, você também pode usar Where r.Role_ID = 3para obter os mesmos resultados.)

Mas eu usaria a primeira consulta e a cláusula WHERE que eu conhecia. Em um grande sistema, saber que o Role_ID geralmente será executado mais rapidamente que o texto, pois os dados numéricos são "mais fáceis" e mais eficientes para a maioria dos mecanismos SQL processar os índices.

Quanto aos métodos de contato, fotos ou o que for, eu os faria da mesma maneira:

Attributes
ID    MethodText    ...
1     'TwitterID'
2     'URL'
3     'CellPhone'
4     'Email'
5     'PictureLink'

Role_Attributes
Role_ID  Attribute_ID isRequired
3        5             1
3        4             1
3        3             0

User_Attributes
User_ID  Attribute_ID  AttributeData
1         4            'Joe@Example.com'
1         1            '@joe'
1         3            '555-555-5555'
1         5            'www.example.com/pics/myFace.png'

...e assim por diante. Eles seriam vinculados da mesma maneira que os usuários às funções.

Isso mostra que cada função tem de 0 a muitos atributos, que podem ser opcionais. Cada usuário possui de 0 a muitos atributos, com os dados para esses atributos.

Isso permite adicionar novos atributos à medida que o tempo passa, sem reescrever nenhum código; basta atualizar as tabelas de atributos e funções de atributos para corresponder às suas novas regras. Também permite compartilhar atributos entre funções, sem inserir novamente os mesmos dados para cada usuário. Se duas funções exigirem fotos, elas deverão enviar apenas uma foto para atender a esse requisito.


Ah, acho que isso faz sentido em um aspecto ... mas não sei ao certo como - por exemplo - listaria todos os atores, digamos, seus nomes de usuário (supondo que os dados do ator estejam armazenados em uma tabela separada).
Verism 09/08

Veja minhas edições; Eu adicionei exemplos de consulta.
CaM

Isso é imensamente útil, obrigado. Acho que talvez não tenha sido completamente claro na minha pergunta - desculpe. Eu deveria ter deixado mais claro que as tabelas para cada tipo (ator, diretor etc.) terão seu próprio conjunto exclusivo de atributos relevantes apenas para esse tipo de usuário. Por exemplo, um ator terá um tiro na cabeça (foto), enquanto um escritor não. Desculpas novamente por não ser mais explícito sobre isso.
Verism 09/08

Os métodos de contato parecem ser uma ótima solução.
Verism 09/08

11
Alterou isso para atributos, para atender aos requisitos de fotos, etc. Agora deve ser um ajuste melhor.
CaM
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.