Temos uma grande variedade de tabelas particionadas por mês. Estatísticas incrementais ativadas. Após a coleta programada de estatísticas, a estimativa de cardinalidade torna-se estranha, como
select count(*) from my_table where date >= trunc(sysdate) - 30 and date < trunc(sysdate)
fornece 1,3 milhão de linhas, mas a estimativa é de 20 mil. Somente depois de reunir manualmente as estatísticas, a estimativa se torna precisa. Exemplos de código:
-- Scheduled
dbms_stats.gather_table_stats
(
ownname=> 'ownname',
tabname=> 'tabname' ,
estimate_percent=> DBMS_STATS.AUTO_SAMPLE_SIZE,
cascade=> DBMS_STATS.AUTO_CASCADE,
degree=> 4,
no_invalidate=> DBMS_STATS.AUTO_INVALIDATE,
granularity=> 'AUTO',
method_opt=> 'FOR ALL COLUMNS SIZE AUTO'
);
-- Manual
DBMS_STATS.GATHER_TABLE_STATS
(
ownname => '"ownname"',
tabname => '"tabname"',
partname => '"partname"',
method_opt => 'FOR COLUMNS DATE SIZE 254',
estimate_percent => 1
);
Outras tabelas particionadas estão ok.
As diferenças entre esta tabela e outras são (como sabemos):
- Havia inserções erradas nesta tabela. A maioria das datas está entre 2014 e 2023, mas há algumas linhas com 1970 e 2024 (não podemos alterá-las). Também há uma partição vazia com 2045. Tentamos recriar isso, mas não obtivemos o mesmo comportamento.
- Mexemos nos histogramas, removemos alguns criados automaticamente e criamos manualmente alguns úteis baseados em funções. Mas em USER_TAB_COL_STATISTICS e USER_TAB_HISTOGRAMS os histogramas para a coluna DATE estavam presentes.
O que pode causar tal comportamento? Como podemos arranjá-lo?
Este é um problema comum com datas ascendentes em tabelas particionadas. A primeira coisa é verificar se as estatísticas incrementais estão funcionando.
Você deverá ver "INCREMENTAL" para cada uma das colunas. Caso contrário, você precisará fazer com que as estatísticas incrementais funcionem corretamente. Para que as estatísticas incrementais funcionem, você deve definir estas opções:
Você deve então coletar estatísticas com essas configurações uma vez, o que inicialmente fará uma coleta global. Pode ser uma boa ideia excluir as estatísticas da tabela primeiro para começar do zero. Então , as coletas subsequentes devem reunir apenas partições obsoletas e usá-las para estimar os valores mínimos/máx/distintos da coluna global.
Além disso, você não deseja histogramas em uma coluna de data, a menos que tenha um valor mágico (como 31/12/9999) que esteja superrepresentado ou uma única linha com uma data falsa que remonta a 1970 ou 1900 ou alguma data boba até agora, uma média mínima/máxima simples estaria realmente errada. Os histogramas são para distorções, não para valores distribuídos uniformemente, como acontece com a maioria das colunas de data. Acho que me lembro de ter lido que os histogramas substituem as estatísticas incrementais, então esse pode ser outro motivo para excluí-los das colunas de datas sempre crescentes. Em nosso big data warehouse não temos histogramas em colunas de data, especialmente aquelas que são chaves de particionamento. Contamos apenas com incremental e geralmente funciona muito bem.
Supondo que suas estatísticas incrementais estejam funcionando, o próximo passo é verificar se a coleta de estatísticas está acontecendo tarde demais (a consulta incorreta pode ser executada logo após a modificação/carregamento principal da partição, antes que as estatísticas tenham a chance de serem coletadas nos novos dados). Para verificar isso, observe
LAST_ANALYZED
e compare com a hora de início da consulta incorreta e a hora do último grande carregamento de dados.Você também pode verificar se o valor máximo da coluna de data está aproximadamente correto. Você terá que decodificá-lo a partir de seu valor bruto. Aqui está um conjunto de funções que você pode usar para exibir o valor mínimo/máximo de suas colunas:
Agora consulte as estatísticas:
Execute isso quando sua consulta for ruim (antes de coletar estatísticas) para ver se o valor máximo é significativamente menor do que deveria (e em datas que podem estar algumas semanas atrasadas ou mais). Esta é outra indicação de que as estatísticas não estão sendo coletadas a tempo. Talvez seja necessário ajustar sua janela de coleta de estatísticas ou adicionar uma chamada manual de estatísticas (sem substituir nenhum parâmetro) ao código que carrega os dados, logo após a conclusão do carregamento.
Se tudo mais falhar e você não conseguir ajustar o tempo das estatísticas com os padrões do seu aplicativo, você sempre pode jogar a toalha de estatísticas e apenas sugerir a consulta com um ou dica
CARDINALITY
paraOPT_ESTIMATE
dizer ao Oracle para esperar um milhão de linhas dessa tabela: