Design de banco de dados pela primeira vez: estou fazendo engenharia excessiva? [fechadas]

246

fundo

Sou estudante do primeiro ano do ensino médio e trabalho em meio período na pequena empresa de meu pai. Eu não tenho nenhuma experiência no desenvolvimento de aplicativos do mundo real. Eu escrevi scripts em Python, alguns cursos em C, mas nada como isso.

Meu pai tem uma pequena empresa de treinamento e atualmente todas as aulas são agendadas, gravadas e acompanhadas por um aplicativo da Web externo. Há um recurso de exportação / "relatórios", mas é muito genérico e precisamos de relatórios específicos. Não temos acesso ao banco de dados real para executar as consultas. Me pediram para configurar um sistema de relatórios personalizado.

Minha idéia é criar as exportações genéricas de CSV e importá-las (provavelmente com Python) para um banco de dados MySQL hospedado no escritório todas as noites, de onde eu possa executar as consultas específicas necessárias. Não tenho experiência em bancos de dados, mas entendo o básico. Eu li um pouco sobre criação de banco de dados e formas normais.

Podemos começar a ter clientes internacionais em breve, então quero que o banco de dados não exploda se / quando isso acontecer. Atualmente, também temos duas grandes corporações como clientes, com diferentes divisões (por exemplo, empresa-mãe da ACME, divisão de assistência médica da ACME, divisão de cuidados corporais da ACME)

O esquema que criei é o seguinte:

  1. Da perspectiva do cliente:
    • Clientes é a tabela principal
    • Os clientes estão vinculados ao departamento para o qual trabalham
      • Os departamentos podem estar espalhados por um país: RH em Londres, Marketing em Swansea etc.
      • Os departamentos estão vinculados à divisão de uma empresa
    • As divisões estão vinculadas à controladora
  2. Da perspectiva das classes:
    • Sessões é a tabela principal
      • Um professor está vinculado a cada sessão
      • Um statusid é fornecido para cada sessão. Por exemplo, 0 - Concluído, 1 - Cancelado
      • As sessões são agrupadas em "pacotes" de tamanho arbitrário
    • Cada pacote é atribuído a um cliente

Eu "projetei" (mais como rabiscado) o esquema em um pedaço de papel, tentando mantê-lo normalizado no terceiro formulário. Depois, pluguei-o no MySQL Workbench e tudo ficou bonito para mim:
( Clique aqui para gráficos em tamanho normal )

texto alternativo
(fonte: maian.org )

Consultas de exemplo que estarei executando

  • Quais clientes com crédito ainda estão inativos (aqueles sem aula agendada no futuro)
  • Qual é a taxa de participação por cliente / departamento / divisão (medida pelo ID do status em cada sessão)
  • Quantas aulas um professor teve em um mês
  • Sinalizar clientes com baixa taxa de participação
  • Relatórios personalizados para departamentos de RH com taxas de participação de pessoas em sua divisão

Questões)

  • Isso é superengenharia ou estou seguindo o caminho certo?
  • A necessidade de ingressar em várias tabelas para a maioria das consultas resultará em um grande impacto no desempenho?
  • Eu adicionei uma coluna 'dura-sessão' aos clientes, pois provavelmente será uma consulta comum. É uma boa ideia ou devo manter o banco de dados estritamente normalizado?

Obrigado pelo seu tempo

Bob Esponja
fonte
131
Caro estudante do primeiro ano do ensino médio: continue usando o StackOverflow. Sua pergunta é interessante, bem escrita e útil. Em outras palavras, você está no top 1% dos solicitantes de pergunta.
Adam Crossland
Uma divisão pode conter outras divisões? Se esse for o caso, uma tabela "has" pode ser usada para vincular a Divisão à Divisão pela qual ela está contida.
Mark Schultheiss
Obrigado pelos gentis comentários :) Mark Vou precisar revisar a documentação deste projeto novamente, mas acho que não identificamos esse caso. Obrigado por apontar isso.
Bob esponja
1
Não gosto das suas convenções de nomenclatura de chave primária. mesa divisionstem coluna chamada divisionid. Você não acha isso redundante? Apenas cite id. também os nomes das suas tabelas, incluindo _has_: eu removeria isso e nomearia apenas por exemplo cities_departments. suas DATETIMEcolunas devem ser do tipo, a TIMESTAMPmenos que sejam valores de entrada do usuário. Eu acho que é uma boa ideia ter as tabelas citiese countries. você pode ter problemas para limitar as tabelas a uma única status. considerar o uso de um INTe realizar comparações bit a bit em ele- assim que você pode segurar mais significado lá
james
@binnyb Há muitos argumentos sobre o uso de id como o nome da chave primária que as pessoas devem considerar antes de decidir.
Jedi

Respostas:

42

Mais algumas respostas para suas perguntas:

1) Você está praticamente no alvo para alguém que está abordando um problema como este pela primeira vez. Penso que os indicadores de outras pessoas sobre essa questão até agora praticamente a cobrem. Bom trabalho!

2 e 3) O desempenho atingido dependerá em grande parte de ter e otimizar os índices corretos para suas consultas / procedimentos específicos e, mais importante, do volume de registros. A menos que você esteja falando de mais de um milhão de registros em suas tabelas principais, você parece estar no caminho de ter um design suficientemente mainstream para que o desempenho não seja um problema em hardware razoável.

Dito isto, e isso se relaciona à sua pergunta 3, com o início, você provavelmente não deveria se preocupar muito com desempenho ou hiper-sensibilidade à ortodoxia da normalização aqui. Este é um servidor de relatório que você está construindo, não um back-end de aplicativo baseado em transações, que teria um perfil muito diferente em relação à importância do desempenho ou da normalização. Um banco de dados que faz backup de um aplicativo de inscrição e agendamento ao vivo deve estar atento às consultas que levam segundos para retornar dados. Não apenas uma função de servidor de relatório tem mais tolerância para consultas complexas e demoradas, mas as estratégias para melhorar o desempenho são muito diferentes.

Por exemplo, em um ambiente de aplicativo baseado em transações, suas opções de melhoria de desempenho podem incluir a refatoração de procedimentos armazenados e estruturas de tabela até o enésimo grau, ou o desenvolvimento de uma estratégia de armazenamento em cache para pequenas quantidades de dados solicitados com frequência. Em um ambiente de relatório, você certamente pode fazer isso, mas pode ter um impacto ainda maior no desempenho, introduzindo um mecanismo de captura instantânea em que um processo agendado é executado e armazena relatórios pré-configurados e seus usuários acessam os dados da captura instantânea sem estresse na camada db por solicitação.

Tudo isso é um discurso longo para ilustrar que os princípios e truques de design que você emprega podem diferir, dado o papel do banco de dados que você está criando. Espero que seja útil.

Tom Crowe
fonte
1
1. Obrigado, isso é tranquilizador! 2 e 3. Ainda não sei como os índices funcionam, é algo que planejei ler. Se algum dia tivermos o "problema" de atingir um milhão de registros, provavelmente haverá um orçamento para contratar desenvolvedores experientes: P Obrigado pela compreensão das diferentes funções de banco de dados existentes, é tudo novo para mim e muito interessante saber. Examinarei os instantâneos, pois o que você descreve é ​​basicamente o objetivo final do projeto.
Bob esponja
Se você entende as tabelas, os fundamentos dos índices são bastante fáceis. Conceitualmente, um índice pode ser (e geralmente é) implementado como uma tabela com poucas colunas cujo conteúdo é copiado da tabela principal e uma referência para a tabela principal, cujas linhas são classificadas keot para facilitar a acessibilidade. B + Tree é o arranjo de índice mais comum, mas as otimizações de índice são onde os grandes players têm suas tecnologias diferenciadoras, então fica obscuro se você tentar aplicar a analogia muito profundamente.
Pojo-guy
14

Você tem a ideia certa. No entanto, você pode limpá-lo e remover algumas das tabelas de mapeamento (possui *).

O que você pode fazer é, na tabela Departamentos, adicionar CityId e DivisionId.

Além disso, acho que está tudo bem ...

Reverendo Gonzo
fonte
4
Acho que ele precisa das tabelas de mapeamento se quiser reutilizar uma definição de departamento em diferentes divisões ou cidades.
Jacob G
1
Sim, eu concordo ..... mas parecia que um departamento só poderia estar em uma cidade / divisão. Se não, então o que ele tinha estava definitivamente correto.
Reverendo Gonzo
Eu tenho um artigo wiki que escrevi com uma "especificação" no escritório, vou ter que lê-lo novamente, mas Jacob G está correto, IIRC, existem alguns departamentos que abrangem divisões. Um departamento de RH dos pais da ACME para os cuidados de saúde e cuidados com o corpo da ACME Se eu puder simplificá-lo, certamente o farei, obrigado pela sugestão.
Bob esponja
6

As únicas mudanças que eu faria são:
1- Altere seu VARCHAR para NVARCHAR; se você estiver indo para o exterior, poderá desejar um código único.

2- Altere os IDs int para GUIDs (uniqueidentifier), se possível (pode ser apenas minha preferência pessoal). Supondo que você chegue ao ponto em que você possui vários ambientes (dev / test / staging / prod), convém migrar dados de um para o outro. Ter IDs de GUID torna isso significativamente mais fácil.

3- Três camadas para a sua empresa -> Divisão -> Estrutura do departamento pode não ser suficiente. Agora, isso pode ser um excesso de engenharia, mas você pode generalizar essa hierarquia para suportar n níveis de profundidade. Isso tornará algumas de suas consultas mais complexas, de modo que talvez não valha a pena. Além disso, pode ser que qualquer cliente que tenha mais camadas possa ser facilmente "empacotado" nesse modelo.

4- Você também tem um Status na Tabela do Cliente que é um VARCHAR e não possui um link para a tabela Status. Eu esperaria um pouco mais de clareza quanto ao que o Status do Cliente representa.

Jacob G
fonte
1 - Obrigado, estou tendo problemas com diacríticos e UTF8, para os quais postaria outra pergunta. Talvez esse seja o problema. 2- Eu li algumas outras perguntas aqui no SO com muitas opiniões conflitantes sobre o assunto, estarei lendo mais sobre o assunto. 3- Vou falar sobre isso com meu pai novamente, olhando para as "especificações" que escrevi e ver se é algo que devemos analisar. --Cont'd próximo comentário
bob esponja
4- Não entrei na questão principal por questões de concisão: o status no cliente é se eles estão ativos (têm sessões restantes) ou inativos (não há sessões restantes). Com mais clareza, você quer dizer um nome mais descritivo para a coluna? Por exemplo, enrolment_status? Obrigado pela sua contribuição.
Bob esponja
re # 4- Além do seu nome mais claro, se houver apenas dois estados, ativo / inativo, por que não torná-lo apenas uma coluna?
Jacob G
3
Discordo sobre os GUIDs, estremece. Eles podem ser horríveis para desempenho. Não os use, a menos que você precise substituí-lo.
HLGEM
1
O desempenho só entra em jogo quando você está falando de dezenas de milhões de linhas em uma tabela. Se você tiver esse tipo de estrutura, poderá mitigá-lo com guias seqüenciais e indexação de criativos. Caso contrário, "desempenho" é um arenque vermelho ao descontar GUIDs.
Jacob G
6

Não. Parece que você está projetando com um bom nível de detalhe.

Penso que Países e Empresas são realmente a mesma entidade em seu projeto, assim como Cidades e Divisões. Eu me livraria das tabelas Países e Cidades (e Cities_Has_Departments) e, se necessário, adicionaria um sinalizador booleano IsPublicSector à tabela Companies (ou uma coluna CompanyType se houver mais opções do que simplesmente Setor Privado / Setor Público).

Além disso, acho que há um erro no uso da tabela Departamentos. Parece que a tabela Departamentos serve como referência aos vários tipos de departamentos que cada divisão de clientes pode ter. Nesse caso, deve ser chamado de DepartmentTypes. Mas seus clientes (que são, presumo, participantes) não pertencem a um TIPO de departamento, eles pertencem a uma instância de departamento real de uma empresa. Como está agora, você saberá que um determinado cliente pertence a um departamento de RH em algum lugar, mas não a qual!

Em outras palavras, os Clientes devem estar vinculados à tabela que você chama de Divisions_Has_Departments (mas que eu chamaria simplesmente de Departamentos). Se for assim, você deve recolher Cidades em Divisões, conforme discutido acima, se desejar usar a integridade referencial padrão no banco de dados.

Larry Lustig
fonte
A tabela de países é para se / quando tivermos clientes que operam em mais de um país e que possuem um departamento de RH diferente para cada um. Dessa forma, podemos criar relatórios com dados do país em que o departamento com o qual estamos lidando opera. O mesmo para departamentos e cidades, acho que temos um cliente que possui depósitos de RH separados. para as duas cidades em que eles têm escritórios principais. Ou, pelo menos, esse foi o motivo, vou me sentar e repensar para ver se eles são realmente necessários. Não tinha pensado em CompanyType, vou descobrir se é algo que precisamos rastrear.
Bob esponja
RE: depts table, meu pensamento original era usá-lo como departamentos reais, com o nome do departamento sendo o tipo. Não me ocorreu apenas ter tipos de departamento, o que parece mais lógico. Sobre saber a que departamento e a quem alguém pertence, pensei que ter o departamento vinculado a uma cidade e divisão (que está vinculada a uma empresa) teria funcionado. Eu estava errado? Por colapsar Cidades em Divisões, algumas Divisões abrangem várias cidades e, talvez, até países. Vou investigar novamente. Obrigado pela sua contribuição.
Bob esponja
5

A propósito, vale a pena notar que, se você já está gerando CSVs e deseja carregá-los em um banco de dados mySQL, LOAD DATA LOCAL INFILE é seu melhor amigo: http://dev.mysql.com/doc/refman/5.1/ pt-br / load-data.html . Também vale a pena examinar o Mysqlimport, e é uma ferramenta de linha de comando que é basicamente um bom invólucro em torno do carregamento de dados infile.

jrheard
fonte
3

A maioria das coisas já foi dita, mas sinto que posso acrescentar uma coisa: é bastante comum que desenvolvedores mais jovens se preocupem com o desempenho um pouco demais, e sua pergunta sobre a junção de tabelas parece ir nessa direção. Este é um anti-padrão de desenvolvimento de software chamado ' Otimização prematura '. Tente banir esse reflexo da sua mente :)

Mais uma coisa: você acredita que realmente precisa das tabelas de 'cidades' e 'países'? Ter uma coluna 'cidade' e 'país' na tabela de departamentos é suficiente para seus casos de uso? Por exemplo, seu aplicativo precisa listar departamentos por cidade e cidades por país?

Hans Westerbeek
fonte
1
Por mais que eu tente, ele continua calculando grande O do helloworld.c, otimizando As tabelas de cidades e países meio que surgiram quando eu estava seguindo as etapas para obter um banco de dados 3NF. Eu acho que a vantagem que eles oferecem é coerência para nomes de cidades / países. Como se tivéssemos um cliente em Munique e, por alguma razão, quem entra com um novo aluno no sistema de agendamento decide chamá-lo de Munique, em vez de Munique, como para os alunos anteriores. Também podemos precisar listar os departamentos por cidade, vou ter que verificar. Obrigado.
Bob esponja
2
A otimização na fase de design de um banco de dados é fundamental! Não é uma otimização prematura, pois os bancos de dados são significativamente mais difíceis de refacotar quando eles têm milhões de registros.
HLGEM
1
Eu não disse que ele não deve salientar-teste seu projeto :)
Hans Westerbeek
3

Após comentários com base na função de especialista em Business Intelligence / Reporting e gerente de estratégia / planejamento:

  1. Eu concordo com a direção de Larry acima. IMHO, não é muito exagerado, algumas coisas parecem um pouco fora do lugar. Para simplificar, eu marcaria o cliente diretamente em uma ID da empresa, Descrição do departamento, Descrição da divisão, ID do tipo de departamento, ID do tipo de divisão. Use o ID do tipo de departamento e o ID do tipo de divisão como referências às tabelas de pesquisa e aos campos internos de relatório / análise para obter consistência a longo prazo.

  2. A tabela de pacotes contém a coluna "Crédito". Isso não deveria estar realmente vinculado à tabela da base do cliente; portanto, se houver muitos pacotes, você poderá ver quanto crédito resta para as classes futuras? O aplicativo pode cuidar do cálculo e armazená-lo centralmente na tabela Cliente.

  3. As informações da empresa podem usar muitos outros campos, incluindo o endereço óbvio / telefone / etc. em formação. Eu também estaria preparado para adicionar as colunas D&B "DUNs" (Site / Filial / Ultimate) a longo prazo, Dun e Bradstreet (D&B) têm um enorme catálogo de empresas e você encontrará mais tarde, no futuro, suas informações são muito úteis para relatórios / análises. Isso resolverá o problema de divisão múltipla que você mencionou e permitirá que você monte sua hierarquia para sub / division / branches / etc. de grandes corpos.

  4. Você não menciona quantos registros com os quais você trabalhará, o que poderia implicar em se preparar para uma grande iniciativa de desenvolvimento que poderia ter sido feita mais rapidamente e com muito menos dores de cabeça com o software de "relatório" pré-empacotado. Se você não está lidando com um grande banco de dados (<65000) de linhas, verifique se o MS-Access, OpenOffice (Base) ou soluções relacionadas de relatórios / aplicativos de desenvolvimento não conseguiram. Eu mesmo uso o software APEX gratuito da Oracle, ele vem com o banco de dados gratuito Oracle XE, basta baixá-lo do site.

  5. FYI - insight de relatórios: para bancos de dados grandes, normalmente você tem duas instâncias de banco de dados a) banco de dados de transações para registrar cada registro detalhado. b) banco de dados de relatórios (data mart / data warehouse) alojado em uma máquina separada. Para obter mais informações, pesquise no Google o esquema Star e o esquema Snowflake.

Saudações.

Vai
fonte
1. Você quer dizer adicionar todas essas colunas à tabela do cliente? Eu acho que isso quebraria a normalização e também dificultaria a manutenção da consistência. Porém, não tenho certeza se entendi direito. 2. Os pacotes são seqüenciais, apenas o pacote mais recente pode ter créditos pendentes; portanto, não é necessário rastrear vários pacotes. Você ainda recomendaria armazená-lo na tabela do cliente nesse caso? 3. Parece que será muito útil descobrir a estrutura das empresas clientes, analisarei obrigado.
Bob esponja
4. Vou ter que verificar o número de clientes e sessões que esperamos ter no próximo ano, mas parece-me viável que a tabela de sessões atinja tantas linhas em um ano ou mais. Vou analisar o software de relatórios, isso não me ocorreu. 5. Parece que é a situação em que cheguei por acidente; o aplicativo da web será nosso "banco de dados de transações" e este projeto nosso "banco de dados de repetição" :) Obrigado pela sua contribuição.
Bob esponja
1. Sim, adicionando colunas "ID da empresa, Descrição do departamento, Descrição da divisão, ID do tipo de departamento, ID do tipo de divisão" à tabela do cliente. O cliente pertence a uma empresa, um tipo de departamento distinto (TI / Ops / Admin / etc.) Dentro de uma empresa e um tipo de divisão distinto (linhas de negócios de Vendas / RH / Marketing). 2. Apenas acho que o crédito está associado a um cliente ou empresa e não ao pacote de sessões. Esta é uma decisão comercial que você pode tomar.
Will
Larry também mencionou a combinação de empresa e país. Eu concordo totalmente e volto ao ponto referente à referência de D&B. Eu usaria um SiteID ou algo exclusivo para permitir vários locais da mesma empresa e depois vincularia os departamentos a um dos SiteIDs exclusivos.
Will
2

Quero abordar apenas a preocupação de que a associação a várias tabelas apresentará um impacto no desempenho. Não tenha medo de normalizar porque você terá que fazer junções. As junções são normais e esperadas em bases de dados relacionais e são projetadas para lidar com elas também. Você precisará definir os relacionamentos PK / FK (para integridade dos dados, é importante considerar isso no design), mas em muitos bancos de dados os FKs não são indexados automaticamente. Como eles serão usados ​​nas junções, você definitivamente desejará começar indexando o FKS. Os PKs geralmente obtêm um índice na criação, pois precisam ser únicos. É verdade que o design do datawarehouse reduz o número de junções, mas geralmente não se chega ao ponto de data warehouse até que haja milhões de registros necessários para serem acessados ​​em um relatório. Mesmo assim, quase todos os data warehouses começam com um banco de dados transacional para coletar os dados em tempo real e, em seguida, os dados são movidos para o armazém em um horário (noturno ou mensal ou qualquer que seja a necessidade da empresa). Portanto, este é um bom começo, mesmo se você precisar projetar um data warehouse mais tarde para melhorar o desempenho do relatório.

Devo dizer que seu design é impressionante para um aluno do primeiro ano do ensino médio.

HLGEM
fonte
1

Não é exagerado, é assim que eu abordaria o problema. A associação é boa, não haverá muito impacto no desempenho (é completamente necessário, a menos que você des Normalize o banco de dados, o que não é recomendado!). Para status, veja se você pode usar um tipo de dados enum para otimizar essa tabela.

Chris Dennett
fonte
enums são más. Toda vez que você precisar estender a enumeração, precisará reconstruir sua tabela - o que é aceitável até que sua tabela tenha muitos GB de tamanho.
Martin Martin
Obrigado pela contribuição e sugestão Chris, eu estava preocupado que eu estaria criando um monstro excessivamente complexo. Martin, os status são bem definidos e estáticos: basicamente 0-classe completa, 1-classe cancelada, 2-não apareceu. Eu acho que esses três cobrem qualquer resultado possível de uma classe. Ainda é uma má idéia usar enums neste caso?
Bob esponja
Isso parece perfeito para um enum, na minha mente. Todos os resultados possíveis são satisfeitos com antecedência. Um int também é bom, o que você pode representar por um enum ou ints estáticos no seu aplicativo. Realmente não importa :) Enums são mais agradáveis ​​de ver se você editar seu banco de dados usando alguma ferramenta.
Chris Dennett
As enumerações podem ser problemáticas (talvez o mal seja uma palavra muito forte) quando você tem tabelas grandes que devem estar on-line 24x7 e a enumeração precisa ser alterada. Como você está repovoando as tabelas do zero - não se preocupe. Dado um conjunto de dados pequeno o suficiente, você também pode usar seqüências de caracteres.
Martin Martin
1

Eu trabalhei no domínio treinamento / escola e pensei em ressaltar que geralmente há uma relação M: 1 entre o que você chama de "sessões" (instâncias de um determinado curso) e o próprio curso. Em outras palavras, seu catálogo oferece o curso ("espanhol 101" ou o que for), mas você pode ter duas instâncias diferentes durante um único semestre (Tu-Th ministrado por Smith, Wed-Fri ministrado por Jones).

Fora isso, parece um bom começo. Aposto que você descobrirá que o domínio do cliente (gráficos que levam a "clientes") é mais complexo do que o modelado, mas não exagere até que você tenha alguns dados reais para guiá-lo.

Larry OBrien
fonte
Se eu o entendi corretamente, não é bem assim. Os "cursos" são apenas grupos de sessões subseqüentes. Não é um sistema tradicional semestral. Não consigo pensar em mais nada que possa ser adicionado ao domínio do cliente. Você tem algum exemplo? Também estava preocupado de ter exagerado já com a complexidade, feliz por não ser o caso :) Obrigado pela sua contribuição.
Bob esponja
0

Algumas coisas vieram à mente:

  1. As mesas pareciam estar voltadas para os relatórios, mas não realmente administrando o negócio. Eu acho que quando um cliente se inscreve, há essencialmente um pedido sendo feito para o cliente participando de uma lista de sessões, e esse pedido pode ser para vários funcionários em uma empresa. Parece que uma tabela de "pedidos" estaria realmente no centro do seu sistema e direcionaria sua captura de dados e eventuais relatórios. (Compare os documentos em papel que você está usando para administrar os negócios com o design do banco de dados para verificar se há uma correspondência lógica.)

  2. As empresas geralmente não têm divisões. Às vezes, os funcionários alteram divisões / departamentos, talvez até no meio da sessão. Às vezes, as empresas adicionam / excluem / renomeiam divisões / departamentos. Certifique-se de que o possível conteúdo em tempo real de alteração de suas tabelas não torne difícil o relatório / agrupamento subsequente. Com tantos dados de contato divididos em tantas tabelas, talvez você precise impor uma validação muito estrita da entrada de dados para manter seus relatórios significativos e inclusivos. Por exemplo, quando um novo cliente é adicionado, verifique se a empresa / divisão / departamento / cidade corresponde aos mesmos valores que seus colegas de trabalho.

  3. O conceito de "pacotes" não está claro.

  4. Como você indica que é uma pequena empresa, seria surpreendente se o desempenho fosse um problema, considerando a velocidade e a capacidade das máquinas atuais.

joe snyder
fonte