Seus dados estão mal cluster .
O InnoDB armazenará linhas com PKs "próximos" fisicamente próximos. Como suas tabelas filhas usam PKs substitutos, suas linhas serão armazenadas aleatoriamente. Quando chega a hora de fazer cálculos para uma determinada linha na tabela "mestre", o DBMS deve pular em todos os lugares para reunir as linhas relacionadas das tabelas filhas.
Em vez de chaves substitutas, tente usar chaves mais "naturais", com o PK do pai na borda principal, semelhante a isto:
score_adjustments:
entry_id: INT(11), FOREIGN KEY (entries.id)
created: DATETIME
amount: INT(4)
PRIMARY KEY (entry_id, created)
rating_adjustments:
entry_id: INT(11), FOREIGN KEY (entries.id)
rating_no: INT(11)
rating: DOUBLE
PRIMARY KEY (entry_id, rating_no)
NOTA:Isso pressupõe
created
a resolução de é boa o suficiente e o rating_no
foi adicionado para permitir várias classificações por entry_id
. Este é apenas um exemplo - você pode variar os PKs de acordo com suas necessidades. Isso "forçará" as linhas pertencentes ao mesmo
entry_id
sejam armazenados fisicamente próximos, de modo que um SUM ou AVG possa ser calculado por apenas uma varredura de intervalo na chave PK/clustering e com muito poucas E/Ss. Como alternativa (por exemplo, se você estiver usando o MyISAM que não suporta clustering), cobrir a consulta com índices para que as tabelas filhas não sejam tocadas durante a consulta.
Além disso, você pode desnormalizar seu design e armazenar em cache os resultados atuais na tabela pai:
- Armazene SUM(score_adjustments.amount) como um campo físico e ajuste-o por meio de acionadores sempre que uma linha for inserida, atualizada ou excluída de
score_adjustments
. - Armazenar SUM(rating_adjustments.rating) como "S" e COUNT(rating_adjustments.rating) como "C". Quando uma linha é adicionada a
rating_adjustments
, adicione-o a S e incremente C. Calcule S/C em tempo de execução para obter a média. Lide com atualizações e exclusões de maneira semelhante.