Lendas de Oracle Tuning – SQL: COUNT(1) x COUNT(*)

Atlantida

O que é mais rápido? COUNT(1), COUNT(*), ou seja lá o que for?

Não importa. Você está se preocupando com a coisa errada.

Executei os seguintes COUNTs, em trace 10053 (o Trace do CBO).

SQL> CONN SCOTT/TIGER
Connected.
SQL> ALTER SESSION SET EVENTS '10053 TRACE NAME CONTEXT FOREVER, LEVEL 1';

Session altered.

SQL> SELECT COUNT(EMPNO) FROM EMP;

COUNT(EMPNO)
------------
	  14

SQL> SELECT COUNT(1) FROM EMP;

  COUNT(1)
----------
	14

SQL> SELECT COUNT(2) FROM EMP;

  COUNT(2)
----------
	14

SQL> SELECT COUNT(*) FROM EMP;

  COUNT(*)
----------
	14

SQL> SELECT COUNT(ROWID) FROM EMP;

COUNT(ROWID)
------------
	  14

SQL> SELECT COUNT(ENAME) FROM EMP;

COUNT(ENAME)
------------
	  14

SQL> SELECT COUNT(SAL) FROM EMP;

COUNT(SAL)
----------
	14

SQL>

Os SQLs com COUNT(EMPNO), COUNT(1) e COUNT(2) foram Internamente alterados, silenciosamente para COUNT(*), e mantida a coluna original como um ALIAS (para não surpreender sua aplicação).

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(EMPNO)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(1)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(2)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

Mesmo o COUNT(*), foi alterado para utilizar o ALIAS, mesmo já sendo igual à coluna.

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

Já o ROWID (que você não deveria mesmo utilizar) não foi alterado para COUNT(*).

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT("EMP".ROWID) "COUNT(ROWID)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

E as colunas ENAME e SAL, que não possuem uma CONSTRAINT do tipo PK ou UNIQUE, também foram mantidas, pois o COUNT(*) poderia trazer um resultado potencialmente diferente, caso houvesse alguma linha com valor NULL nestas colunas

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT("EMP"."ENAME") "COUNT(ENAME)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT("EMP"."SAL") "COUNT(SAL)" FROM "SCOTT"."EMP" "EMP"
kkoqbc: optimizing query block SEL$1 (#0)

5 comments

  1. Portilho nunca tinha pensado nisso rsrs dai resolvi tirar essa pequena dúvida numa tabela simples aqui agora com 104 colunas .
    Descobri algo inusitado aqui :
    Tenho uma tabela com 2911 linhas e dando um select com count(*) o resultado veio em 0,015 segundos e
    com o count(1) retornou em 0,016 segundos .
    Em outras tentativas cheguei em 0,016 para count(*) e count(1).
    Algo me deixou curioso nessa história em questões de estatísticas ,seguinte o count(*) retorna apenas registros not null ,pois bem fui dar uma olhada na view all_tables e o número de linhas dessa tabela do count realizado está apresentando 2803 linhas ,muito estranho ,imagino que as estatísticas da tabela não estão atualizadas.

    1. Em apenas 2911 linhas não dá para perceber a diferença. Veja que a diferença foi de 1 milésimo de segundo.
      Sim, a ALL_TABLES é baseada em estatísticas, então elas estão levemente erradas. Mas a diferença é pouca, dificilmente isto iria influenciar um plano de execução.

  2. Só pra não passar batido essa tabela tem colunas indexadas e ainda com PK e penso eu que o Oracle fez Fast Full Scan invés do Full Table Scan.

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.