Quando trabalhamos com bancos de dados relacionais, é comum nos depararmos com situações onde precisamos contar registros relacionados em uma tabela.
Primeiramente vamos considerar as seguintes tabelas:
CREATETABLEorders(order_idSERIALPRIMARYKEY,order_dateDATE,customerVARCHAR(100));CREATETABLEorder_items(item_idSERIALPRIMARYKEY,order_idINT,productVARCHAR(100),quantityINT,FOREIGNKEY(order_id)REFERENCESorders(order_id));
Temos uma tabela deorders
e queremos contar quantos itens cada pedido possui. Uma abordagem comum seria usar uma subquery com COUNT:
SELECTo.order_id,(SELECTCOUNT(*)FROMorder_itemsoiWHEREoi.order_id=o.order_id)AStotal_itemsFROMorderso;
Essa query funciona, mas pode se tornar ineficiente em cenários com grandes volumes de dados, pois a subquery é executada para cada linha da tabelaorders
.
Aqui entra adesnormalização!
A desnormalização em bancos de dados é o processo de introduzir redundância em um banco de dados previamente normalizado, com o objetivo de melhorar o desempenho em operações de leitura (consultas). Envolve a adição de dados duplicados ou a combinação de tabelas para reduzir o número de operações necessárias (joins, counts...), o que pode acelerar a recuperação de informações em sistemas onde a leitura é mais frequente que a escrita.
Agora sabemos o que é desnormalização como podemos simplificar nossa query?
Em vez de contar os itens toda vez que executamos a query, podemos adicionar um campototal_items diretamente na tabelaorders. E sempre que um item for adicionado ou removido, incrementamos ou decrementamos esse campo.
Como fazer isso?
- Adicione o campo
total_items
na tabelaorders
:
ALTERTABLEordersADDCOLUMNtotal_itemsINTDEFAULT0;
Atualize o campo
total_items
sempre que um item for adicionado ou removidoSua query fica muito mais simples e eficiente:
SELECTorder_id,total_itemsFROMorders;
Vamos inserir dados para podermos validar nossa proposta, podemos inserir uma massa de dados com a seguinte função:
CREATEORREPLACEFUNCTIONgenerate_sample_data()RETURNSVOIDAS$$DECLAREorder_countINT:=200;items_per_orderINT:=10;current_order_idINT;random_customerVARCHAR(100);random_productVARCHAR(100);random_quantityINT;BEGINFORiIN1..order_countLOOPrandom_customer:='Customer '||(floor(random()*1000)::INT);INSERTINTOorders(order_date,customer,total_items)VALUES(current_date-(floor(random()*365)::INT),random_customer,10)RETURNINGorder_idINTOcurrent_order_id;FORjIN1..items_per_orderLOOPrandom_product:='Product '||(floor(random()*100)::INT);random_quantity:=(floor(random()*10)::INT+1;INSERTINTOorder_items(order_id,product,quantity)VALUES(current_order_id,random_product,random_quantity);ENDLOOP;ENDLOOP;END;$$LANGUAGEplpgsql;SELECTgenerate_sample_data();
No script anterior basicamente criamos 200 registros na tabelaorders
e 10 registros na tabelaorder_items
para cadaorder
.
Para compararmos e validarmos nossa hipótese vamos executar nossas queries comexplain analyse
e teremos o seguinte resultado:
Utilizando a subquery:
Podemos ver que executando a consulta com a subquery o execution time é igual a28.983ms
Utilizando o campo criado na desnormalização:
Sem a subquery nosso resultado é0.073ms. Sendo assim397 vezes mais rápido e vale ressaltar quecriamos apenas 200 registros na tabelaorders
, quantomais registros criarmos mais a query com subquery serálenta e a query desnormalizada irá se manter na mesma média.
Vantagens:
Performance: Evita a execução de subqueries repetidas.
Simplicidade: A query principal fica mais limpa e fácil de entender.
Controle: Você tem um controle direto sobre o valor do contador.
Cuidados:
Consistência: É preciso garantir que o campo total_items seja sempre atualizado corretamente.
Concorrência: Em sistemas com alta concorrência, é importante garantir que as atualizações sejam atômicas para evitar inconsistências.
Espero que esse conteúdo tenha sido útil para você!
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse