AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 332460
Accepted
Andy DB Analyst
Andy DB Analyst
Asked: 2023-10-24 16:38:09 +0800 CST2023-10-24 16:38:09 +0800 CST 2023-10-24 16:38:09 +0800 CST

Estimativa de cardinalidade errada após coleta de estatísticas ORACLE

  • 772

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):

  1. 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.
  2. 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?

oracle
  • 1 1 respostas
  • 30 Views

1 respostas

  • Voted
  1. Best Answer
    Paul W
    2023-10-25T09:33:31+08:002023-10-25T09:33:31+08:00

    Este é um problema comum com datas ascendentes em tabelas particionadas. A primeira coisa é verificar se as estatísticas incrementais estão funcionando.

    SELECT notes
      FROM dba_tab_col_statistics
     WHERE table_name = 'MYTABLE'
    

    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:

    GRANULARITY -> AUTO
    INCREMENTAL -> TRUE
    PUBLISH -> TRUE
    ESTIMATE_PERCENT -> DBMS_STATS.AUTO_SAMPLE_SIZE
    APPROXIMATE_NDV_ALGORITHM -> LIKE '%HYPERLOGLOG%'
    INCREMENTAL_LEVEL -> PARTITION
    

    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_ANALYZEDe 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:

    CREATE OR REPLACE FUNCTION CAST_RAW_TO_CHAR (bdr IN raw) 
      RETURN varchar2
      DETERMINISTIC 
    AS  
    BEGIN
      RETURN utl_raw.cast_to_varchar2(bdr);
    EXCEPTION 
      WHEN OTHERS THEN 
        RETURN NULL;
    END CAST_RAW_TO_CHAR;
    /
    CREATE OR REPLACE FUNCTION CAST_RAW_TO_NUMBER (bdr IN raw) 
      RETURN number
      DETERMINISTIC 
    AS  
    BEGIN
      RETURN utl_raw.cast_to_number(bdr);
    EXCEPTION 
      WHEN OTHERS THEN 
        RETURN NULL;
    END CAST_RAW_TO_NUMBER;
    /
    CREATE OR REPLACE FUNCTION CAST_RAW_TO_DATE (bdr IN raw) 
      RETURN date
      DETERMINISTIC 
    AS  
    BEGIN
      RETURN
         date'1-1-1'
         + NUMTOYMINTERVAL(
             100 * (to_number(substr(bdr,1,2), 'xx') - 100) + 
             to_number(substr(bdr,3,2), 'xx') - 101, 
           'year')
         + NUMTOYMINTERVAL(to_number(substr(bdr,5,2), 'xx')-1, 'month')
         + NUMTODSINTERVAL(to_number(substr(bdr,7,2), 'xx')-1, 'day')
         + NUMTODSINTERVAL(to_number(substr(bdr,9,2), 'xx') - 1, 'hour')   
         + NUMTODSINTERVAL(to_number(substr(bdr,11,2), 'xx') - 1, 'minute')   
         + NUMTODSINTERVAL(to_number(substr(bdr,13,2), 'xx') - 1, 'second');
    EXCEPTION 
      WHEN OTHERS THEN 
        RETURN NULL;
    END CAST_RAW_TO_DATE;
    /
    
    CREATE OR REPLACE FUNCTION CAST_RAW_TO_DISPLAY_STRING (bdr IN raw, data_type IN varchar2)
      RETURN varchar2
      DETERMINISTIC 
    AS
    BEGIN
      RETURN CASE WHEN (bdr IS NULL) THEN NULL
                  WHEN (data_type LIKE '%CHAR%') THEN cast_raw_to_char(bdr) 
                  WHEN (data_type = 'NUMBER') THEN TO_CHAR(cast_raw_to_number(bdr))
                  WHEN (data_type = 'DATE' OR data_type LIKE '%TIMESTAMP%') THEN TO_CHAR(cast_raw_to_date(bdr))
             END;
    END;
    /
    

    Agora consulte as estatísticas:

    SELECT /*+ NO_MERGE(cs) NO_MERGE(tc) */
           cs.owner,
           cs.table_name,
           cs.column_name,
           cs.num_distinct,
           tc.data_type,
           cast_raw_to_display_string(cs.low_value,tc.data_type) low_value_display,
           cast_raw_to_display_string(cs.high_value,tc.data_type) high_value_display,
           cs.density,
           cs.num_nulls,
           cs.num_buckets,
           cs.last_analyzed,
           cs.sample_size,
           cs.global_stats,
           cs.user_stats,
           cs.notes,
           cs.avg_col_len,
           cs.histogram,
           cs.scope
      FROM dba_tab_col_statistics cs,
           dba_tab_cols tc
     WHERE cs.table_name = 'MYTABLE'
       AND cs.owner = tc.owner
       AND cs.table_name = tc.table_name
       AND cs.column_name = tc.column_name
    

    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 CARDINALITYpara OPT_ESTIMATEdizer ao Oracle para esperar um milhão de linhas dessa tabela:

    SELECT /*+ CARDINALITY(x,1000000) */ *
      FROM mytable x,
           someothertable y
     WHERE x.joinid = y.id
    
    SELECT /*+ OPT_ESTIMATE(TABLE x ROWS=1000000) */ *
      FROM mytable x,
           someothertable y
     WHERE x.joinid = y.id
    
    • 1

relate perguntas

  • Backups de banco de dados no Oracle - Exportar o banco de dados ou usar outras ferramentas?

  • ORDER BY usando prioridades personalizadas para colunas de texto

  • Interface sqlplus confortável? [fechado]

  • Como encontrar as instruções SQL mais recentes no banco de dados?

  • Como posso consultar nomes usando expressões regulares?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve