mardi 12 juillet 2011

COUNT : passage par un index ou pas

Le COUNT(*) renvoie le nombre total de lignes de la table. Il ne peut donc utiliser qu'un index Bitmap. Par contre il peut utiliser n'importe quel index Bitmap (car pour chaque valeur du Bitmap, on a une image de toutes les lignes - donc des lignes a valeur nulle).
Si la colonne est NOT NULL, alors toutes les valeurs de cette colonne seront renseignées, donc un index B*Tree indexera lui aussi toutes les lignes de la table, il pourra donc être utilisé pour le COUNT(*) dans ce cas uniquement.

Le COUNT(colonne) renvoie le nombre de lignes ayant une valeur non nulle pour la colonne. Il peut donc passer par un index B*Tree (et Bitmap) pour évaluer le résultat.

Le COUNT(n) est équivalent au COUNT(*) mais la méthode recommandée est le COUNT(*).


Exemples

SQL> CREATE TABLE t1 (a NUMBER);

Table created.

SQL> INSERT INTO t1 VALUES (1);

1 row created.

SQL> INSERT INTO t1 VALUES (null);

1 row created.

SQL> INSERT INTO t1 VALUES (3);

1 row created.

SQL> SELECT * FROM T1;

         A
----------
         1

         3

 
Exemple d'utilisation d'index B*Tree avec des NULLs
 

SQL> CREATE INDEX i_t1 ON t1 (a);

Index created.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'t1',no_invalidate=>FALSE);

PL/SQL procedure successfully completed.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM T1;

  COUNT(*)
----------
         3


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'T1' (TABLE) (Cost=3 Card=3)



SQL> SELECT COUNT(1) FROM T1;

  COUNT(1)
----------
         3


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'T1' (TABLE) (Cost=3 Card=3)


Dans les deux exemples précédents, l'index B*Tree ne peut être utilisé car il n'indexe pas la totalité des lignes de la table (valeurs nulles exclues).

SQL> SELECT COUNT(a) FROM T1;

  COUNT(A)
----------
         2


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1 Bytes=13)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FULL SCAN) OF 'I_T1' (INDEX) (Cost=1 Card=3 Bytes
          =39)

Le count(colonne) décompte toutes les lignes ayant une valeur non nulle de cette colonne, donc autorise le passage par un index B*Tree

 
Exemple d'utilisation d'index Bitmap avec des NULLs

SQL> DROP INDEX i_t1;

Index dropped.

SQL> CREATE BITMAP INDEX i_t1 ON t1 (a);

Index created.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'t1',no_invalidate=>FALSE);

PL/SQL procedure successfully completed.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM t1;

  COUNT(*)
----------
         3


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     BITMAP CONVERSION (COUNT) (Cost=1 Card=3)
   3    2       BITMAP INDEX (FAST FULL SCAN) OF 'I_T1' (INDEX (BITMAP
          ))


L'index Bitmap enregistre une image bitmap de toutes les lignes de la table pour une colonne donnée (si index mono colonne), et donc index les valeurs nulles. Par conséquent le count(*) qui décompte toutes les lignes de la table, peut utiliser cet index.

SQL> SELECT COUNT(a) FROM t1;

  COUNT(A)
----------
         2


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1 Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     BITMAP CONVERSION (TO ROWIDS) (Cost=1 Card=3 Bytes=6)
   3    2       BITMAP INDEX (FAST FULL SCAN) OF 'I_T1' (INDEX (BITMAP
          ))

SQL> SET AUTOTRACE OFF


Exemple d'utilisation d'un index B*Tree avec contrainte NOT NULL


SQL> DROP INDEX i_t1;

Index dropped.

SQL> CREATE INDEX i_t1 ON t1 (a);

Index created.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'t1',no_invalidate=>FALSE);

PL/SQL procedure successfully completed.

SQL> DELETE FROM t1 WHERE a IS NULL;

1 row deleted.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM t1;

  COUNT(*)
----------
         2


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'T1' (TABLE) (Cost=3 Card=3)



SQL> SET AUTOTRACE OFF
SQL> ALTER TABLE t1 MODIFY a NOT NULL;

Table altered.

SQL> SET AUTOTRACE ON EXPLAIN
SQL> SELECT COUNT(*) FROM t1;

  COUNT(*)
----------
         2


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FULL SCAN) OF 'I_T1' (INDEX) (Cost=1 Card=3)



SQL> SET AUTOTRACE OFF

Cette fois l'index B*Tree est bien utilisé pour le count(*)

Aucun commentaire:

Enregistrer un commentaire