Coluna duplicada para consultas mais rápidas?

30

O título não faz muito sentido, mas não consegui pensar em um título melhor para esse problema.

Eu tenho as seguintes tabelas

Projetos

  • identidade
  • nome

clientes

  • identidade
  • id_project
  • nome

Pagamentos

  • identidade
  • id_customer
  • encontro
  • soma

Quando um usuário entra no sistema, ele terá acesso a um determinado projeto. Agora, quero listar todos os pagamentos desse projeto e deve ser bem fácil:

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

Minha pergunta é: se não for melhor adicionar uma coluna id_project à tabela de pagamentos dessa maneira, as consultas serão mais fáceis e rápidas.

Gabriel Solomon
fonte
11
portanto, a consulta não é um problema para RDBMSes modernos (ou melhor, use join).
Garik
4
Concorde, obtenha um plano de consulta para subselecionar vs ingressar e veja qual é o melhor #
Gaius
11
Acho que este SO pós vale a pena olhar, uma vez @igor mencionado sobre o uso de associação ou em
CoderHawk

Respostas:

52

Parece que você está perguntando se a desnormalização faz sentido.

Desnormalização é o processo de tentar otimizar o desempenho de leitura de um banco de dados adicionando dados redundantes ou agrupando dados. Em alguns casos, a desnormalização ajuda a encobrir as ineficiências inerentes ao software de banco de dados relacional. Um banco de dados relacional normalizado impõe uma carga pesada de acesso ao armazenamento físico de dados, mesmo que esteja bem ajustado para alto desempenho.

A resposta é sempre "depende", então aqui está minha regra de ouro:

E se ...

  • a quantidade de dados não é grande
  • você não está fazendo uma tonelada de junções já
  • e / ou o desempenho do banco de dados não é atualmente um gargalo

então fique normalizado . Sim, a desnormalização é mais rápida, mas também significa que você possui dados redundantes no sistema - dados que precisam ser mantidos e sincronizados. Não há mais "uma fonte" para esses dados, mas várias fontes que podem se desviar. Isso é arriscado ao longo do tempo, portanto, você não deve fazê-lo, a menos que tenha boas razões para fazê-lo, apoiado por alguns parâmetros de referência.

Eu só desnormalizaria quando ...

  • a quantidade de dados é muito grande
  • as junções são caras e você precisa fazer muitas delas para obter retornos ainda triviais
  • o desempenho do banco de dados é um gargalo e / ou você deseja ir o mais rápido possível

As junções são muito rápidas no hardware moderno, mas nunca são gratuitas.

Jeff Atwood
fonte
9

Seria melhor reescrever a consulta como:

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

Embora isso pareça menos conciso e um bom planejador de consultas veja o que você está tentando fazer e execute sua subconsulta correlacionada como a junção acima, um planejador de consultas ruim pode acabar fazendo uma varredura de índice payments.id_customer(supondo que você tenha um índice relevante ) (ou pior, varredura de tabela) em vez de fazer as coisas da maneira mais eficiente. Mesmo um bom planejador de consultas pode não conseguir ver a otimização se o arranjo dessa consulta estiver envolvido em algo mais complicado. Expressar o relacionamento como uma junção em vez de uma subconsulta pode fazer mais diferença do que alterar sua estrutura de dados.

Como Jeff diz, qualquer desnormalização deve ser considerada com cuidado - isso pode proporcionar um aumento fácil no desempenho, principalmente para fins de geração de relatórios, mas pode levar à inconsistência devido a erros na lógica comercial de suporte.

Como uma observação lateral: obviamente eu não conheço o seu negócio, então poderia estar perdendo alguma coisa, mas o seu relacionamento com a mesa me parece estranho. Elas implicam que você nunca pode ter mais de um projeto com o mesmo cliente, o que geralmente não é verdade na minha experiência, pelo menos por um longo período.

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

ou se estiver sendo menos normalizado (embora eu duvide que seja necessário):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

Claro que isso ainda desconta a possibilidade de um projeto conjunto com dois clientes ...

David Spillett
fonte
3
Primeira regra de desempenho: nunca use * na produção!
Brian Ballsun-Stanton
@ Brian: ponto muito válido. E, além das possíveis implicações no desempenho, evitar * nas cláusulas de seleção também evita problemas com a ordem das colunas nas visualizações na exibição no MSSQL se o sys.depends ficar fora de ordem devido ao uso de DROP VIEW+ CREATE VIEWem vez de ALTER VIEW.
David Spillett
@ Brian eu coloquei * para facilitar a escrita.
Gabriel Solomon
O projeto é pensado mais como um aplicativo independente, com domínio próprio e pertence a clientes diferentes, portanto, um cliente não pode ter a mesma conta em projetos diferentes.
Gabriel Solomon
4

Em alguns bancos de dados, você tem a possibilidade de criar "Visualizações materializadas" em vez de VIEWS complexas com uma grande quantidade de dados, com base em uma consulta complexa. Isso pode ser usado para evitar a desnormalização em um sistema de aplicativo histórico. Se você decidir usar " Vistas materializadas ", é necessário ter uma idéia clara dos métodos de atualização e a quantidade de armazenamento que será usada pela Visualização materializada ...

Christof Prettner
fonte