A situação:
O líder do nosso projeto decidiu usar um banco de dados altamente normalizado como nosso design de banco de dados. O que significa que literalmente todos os campos de uma tabela grande são um ID em vez do valor real. Sua intenção é não ter duplicatas de qualquer tipo, mesmo em lugares onde duplicatas não machucam (primeiros nomes de pessoas, esse tipo de coisa).
No entanto, isso leva a um problema: ao inserir novos dados, precisamos verificar cada subtabela para ver se o valor existe (primeira consulta) e inseri-lo se não existir (segunda consulta) ou então recuperar o ID, fazer isso para literalmente todas as colunas da tabela principal (então 30 vezes ou algo assim), e então podemos criar o objeto que realmente queríamos obter. (São ~60 acessos ao banco de dados para criar um objeto!).
Trabalhamos na primavera, então usamos jdbcTemplate para construir uma conexão de banco de dados, e cada consulta é cara. Quando estamos inserindo milhares de novos registros ou atualizando-os, isso na verdade torna o banco de dados muito lento.
Todo esse processo parece completamente sujo e errado para mim e, portanto, queria perguntar: existe uma maneira melhor? É possível fazer uma subconsulta inserir um valor se não existir, ou não, se existir, e retornar a chave real em ambos os casos, que é usada imediatamente para definir o ID na tabela principal? Existe uma solução elegante para reduzir o número de consultas sem introduzir muito SQL complicado (para o bem dos membros da equipe)?
Um pequeno pensamento...
Formalmente você tem algo parecido com esta estrutura simplificada:
Quando você precisa inserir 2 registros (id_1, val_1_1, val_1_2) e (id_2, val_2_1, val_2_2), você executa:
O mecanismo de
temp
pode ser Memory quando a quantidade de valores inseridos for baixa, e InnoDB ou qualquer outra coisa se o array de dados inserido for enorme.INSERT IGNORE funciona rápido o suficiente no campo indexado UNIQUE. Garante que não haja duplicatas nas tabelas escravas, e que os valores que devem ser inseridos existirão nas escravas durante a inserção na tabela principal.
E a consulta final também deve ser rápida - especialmente quando os campos de tabela temporários também são indexados.
Se você precisar inserir apenas um registro, então você pode, claro, não usar tabela
temp
... mas acho que a uniformidade é mais segura do que uma pequena simplificação.Claro, isso pode ser otimizado. Por exemplo, todas as inserções podem ser unidas em um procedimento armazenado, e você não precisa em "60 acessos ao banco de dados", uma CHAMADA é suficiente. Finalmente você deve executar apenas 3 consultas independentes pela contagem de registros para inserir. E apenas um deles (inserindo em temptable) pode ser enorme (ou mesmo pode ser dividido em vários inserts).
Se você puder agrupar os dados, posso fornecer uma técnica que envolve 2 consultas por lote - uma para inserir novos nomes para o lote, outra para encontrar todos os ids. E não desperdiça ids, como
REPLACE
,INSERT IGNORE
, IODKU, etc.INSERT .. SELECT ..
para mover os dados para a(s) tabela(s) real(is).Ver Normalização em Massa
Isso foi projetado (e aperfeiçoado anos atrás) quando eu precisava colocar grandes quantidades de dados (possivelmente provenientes de muitos clientes) em tabelas e alguma normalização (não total) era necessária.
No começo eu tinha
INSERT IGNORE
, mas rapidamente percebi que era provável que eu ficasse sem ids de auto_increment. Eu não estava disposto a usar 8 bytesBIGINTs
. Afinal, um dos propósitos de normalizá-lo é economizar espaço.