Uma discussão de longa data nos fóruns e grupos de notícias da Oracle tem sido a eficiência de usar count(*) para retornar uma contagem de linhas de uma determinada tabela. Uma nova ruga nessa discussão agora apresenta count(rowid) como uma alternativa mais eficiente; o argumento afirma que count(*) expande toda a lista de colunas, bem como “select * …” e, como tal, pode ser um coletor de recursos quando as colunas CLOB estão presentes na tabela desejada. Vamos olhar para esse argumento e ver se ele se sustenta. Vamos começar criando e preenchendo uma tabela contendo uma coluna CLOB:
SQL>SQL> cria tabela count_test( 2 id number, 3 val varchar2(40), 4 clb clob);Table created.SQL>SQL> begin 2 for z in 1..1000000 loop 3 insert into count_test 4 values (z, 'Registro '||z, 'Valor Clob '||z); 5 voltas finais; 6 7 cometer; 8 fim; 9 Procedimento /PL/SQL concluído com sucesso.SQL>
Em seguida, vamos definir o evento 10053 para despejar o rastreamento do otimizador para que possamos ver como o Oracle planeja executar as consultas count():
SQL> alter session set events ='10053 trace name context forever, level 2';Sessão alterada.
O palco está montado, vamos executar algumas variantes de count() para ver como o Oracle se comporta. Primeiro, executaremos uma contagem direta(*) e exibiremos o plano:
SQL> selecione count(*) de count_test; COUNT(*)---------- 1000000SQL> alter session set events ='10053 trace name context off';Session changes.SQL> Explain plan for select count(*) from count_test;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT-------------------- -------------------------------------------------- -------------------------Valor de hash do plano:371675025-------------------- --------------------+----------------------------- ------+| ID | Operação | Nome | Linhas | Bytes | Custo | Tempo |----------------------------------------+------- ----------------------------+| 0 | SELECIONAR DECLARAÇÃO | | | | 3582 | || 1 | ORDENAR AGREGADO | | 1 | | | || 2 | ACESSO À MESA COMPLETO | COUNT_TEST| 848K | | 3582 | 00:00:43 |---------------------------------------------------+--- -----------------+ Informações de Projeção da Coluna (identificadas pelo ID da operação):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Observação----- - estatísticas dinâmicas usadas:amostragem dinâmica (nível=2)19 linhas selecionadas.SQL>
Observando o arquivo de rastreamento gerado, o Oracle simplesmente usa count(*) como está para retornar os resultados:
Consulta final após transformações:******* UNPARSED QUERY IS ********SELECT COUNT(*) "COUNT(*)" FROM "BING".."COUNT_TEST" "COUNT_TEST" ... ----- Explicar o Despejo de Plano ---------- Tabela de Plano -----============Tabela de Plano============----------------------------------------+-------- ---------------------------+| ID | Operação | Nome | Linhas | Bytes | Custo | Tempo |----------------------------------------+------- ----------------------------+| 0 | SELECIONAR DECLARAÇÃO | | | | 3582 | || 1 | ORDENAR AGREGADO | | 1 | | | || 2 | ACESSO À MESA COMPLETO | COUNT_TEST| 848K | | 3582 | 00:00:43 |---------------------------------------------------+--- --------------------------------+ Nome do Bloco de Consulta / Alias do Objeto (identificado pelo ID da operação):---- -------------------------------------------------- ------1 - SEL$12 - SEL$1 / "COUNT_TEST"@"SEL$1"---------------------------- --------------------------------Informações do Predicado:--------------- ---------SQL>
Não há surpresas; observe que o Oracle não expande o “*” para todas as colunas da tabela — o “*” neste caso indica que todas as linhas devem ser contadas. Se um nome de coluna real tivesse sido fornecido, o Oracle teria contado os valores na coluna especificada. Vejamos agora o que o Oracle faz com uma consulta count(rowid):
SQL> alter session set events ='10053 trace name context forever, level 2';Session changes.SQL> select count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> alter session set events ='10053 trace name context off';Session changes.SQL> Explain plan for select count(rowid) from count_test;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection '));PLAN_TABLE_OUTPUT---------------------------------------------- -------------------------------------------------- ----Valor de hash do plano:371675025--------------------------------------------------+ -----------------------------------+| ID | Operação | Nome | Linhas | Bytes | Custo | Tempo |----------------------------------------+------- ----------------------------+| 0 | SELECIONAR DECLARAÇÃO | | | | 3582 | || 1 | ORDENAR AGREGADO | | 1 | 12 | | || 2 | ACESSO À MESA COMPLETO | COUNT_TEST| 848K | 9941K | 3582 | 00:00:43 |---------------------------------------------------+--- -----------------+ Informações de Projeção da Coluna (identificadas pelo ID da operação):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Observação----- - estatísticas dinâmicas usadas:amostragem dinâmica (level=2)19 linhas selecionado.SQL>
O Oracle gera um valor de rowid para cada linha da tabela, uma operação que consumirá alguns recursos da CPU. Como a consulta retornou aproximadamente ao mesmo tempo que a versão count(*), o desempenho 'hit' parece ser insignificante. Adicionar uma chave primária altera ligeiramente os planos, mas não o texto da consulta:
SQL> altera a tabela count_test adiciona a restrição count_pk chave primária(id);Tabela alterada. SQL>SQL> alter session set events ='10053 trace name context forever, level 2';Sessão alterada.SQL> select count(*) from count_test; COUNT(*)---------- 1000000SQL> alter session set events ='10053 trace name context off';Session changes.SQL> Explain plan for select count(*) from count_test;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT-------------------- -------------------------------------------------- -----------------Valor de hash do plano:371675025------------------------ -------------------------------------------------- | ID | Operação | Nome | Linhas | Custo (%CPU)| Tempo |------------------------------------------------ ---------------| 0 | SELECIONAR DECLARAÇÃO | | 1 | 589 (2)| 00:00:01 || 1 | ORDENAR AGREGADO | | 1 | | || 2 | ÍNDICE VARREDURA COMPLETA RÁPIDA| COUNT_PK | 848K| 589 (2)| 00:00:01 |-------------------------------------------- ------------------------------ Informações de projeção da coluna (identificadas pelo id da operação):---------- -------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Observação----- - estatísticas dinâmicas usadas:amostragem dinâmica (nível=2)19 linhas selecionadas.SQL>SQL>SQL> alter session set events ='10053 trace name context forever, level 2';Session alterada.SQL> selecione count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> alter session set events ='10053 contexto de nome de rastreamento desativado';Sessão alterada.SQL> plano de explicação para selecionar contagem(rowid) de count_test;Explained.SQL> selecionar * da tabela(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT- -------------------------------------------------- ---------------------------------------Valor de hash do plano:371675025------ -------------------------------------------------- ---------------| ID | Operação | Nome | Linhas | Bytes | Custo (%CPU)| Tempo |------------------------------------------------ ----------------------------------| 0 | SELECIONAR DECLARAÇÃO | | 1 | 12 | 589 (2)| 00:00:01 || 1 | ORDENAR AGREGADO | | 1 | 12 | | || 2 | ÍNDICE VARREDURA COMPLETA RÁPIDA| COUNT_PK | 848K| 9941K| 589 (2)| 00:00:01 |-------------------------------------------- -------------------------------------- Informações de projeção da coluna (identificadas pelo id da operação):-- -------------------------------------------------- ------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Observação----- - estatísticas dinâmicas usadas:amostragem dinâmica (nível =2)19 linhas selecionadas.SQL>SQL> spool offcommit;
Os detalhes do rastreamento 10053 não foram alterados após a adição da chave primária.
Parece que duas informações foram coletadas deste experimento - count(rowid) não é melhor que count(*) quando as tabelas contêm colunas CLOB e que count(*) não expande a lista de colunas como “select *” faz (e não há razão para acreditar que deveria).
A prova está no pudim, como diz o velho ditado.
# # #
Veja artigos de David Fitzjarrell