Oracle
 sql >> Base de données >  >> RDS >> Oracle

COUNT(rowid) est-il plus rapide que COUNT(*) ?

Une discussion de longue date dans les forums et les groupes de discussion Oracle porte sur l'efficacité de l'utilisation de count(*) pour renvoyer un nombre de lignes à partir d'une table donnée. Une nouvelle ride dans cette discussion introduit maintenant count(rowid) comme une alternative plus efficace; l'argument indique que count(*) développe la liste complète des colonnes, un peu comme "select * ...", et, en tant que tel, pourrait être un puits de ressources lorsque des colonnes CLOB sont présentes dans la table souhaitée. Examinons cet argument et voyons s'il tient la route. Commençons par créer et remplir une table contenant une colonne CLOB :

SQL>SQL> create table 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, 'Enregistrement '||z, 'Valeur Clob '||z); boucle à 5 extrémités ; 6 7 s'engager ; 8 fin ; 9 Procédure /PL/SQL terminée avec succès.SQL>

Ensuite, définissons l'événement 10053 pour vider la trace de l'optimiseur afin que nous puissions voir comment Oracle prévoit d'exécuter les requêtes count() :

SQL> alter session set events ='10053 contexte de nom de trace pour toujours, niveau 2';Session modifiée.

Le décor est planté, lançons quelques variantes de count() pour voir comment se comporte Oracle. Tout d'abord, nous allons exécuter un comptage direct(*) et afficher le plan :

SQL> select count(*) from count_test ; COUNT(*)---------- 1000000SQL> alter session set events ='10053 trace name context off';Session modified.SQL> expliquer le plan pour sélectionner count(*) from count_test;Explained.SQL> select * de table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------- -------------------------------------------------- -------------------------Valeur de hachage du plan :371675025-------------------- --------------------+-------------------------------------- ------+| identifiant | Opération | Nom | Lignes | Octets | Coût | Heure |----------------------------------------+------- ----------------------------+| 0 | SÉLECTIONNER L'INSCRIPTION | | | | 3582 | || 1 | TRIER L'AGRÉGAT | | 1 | | | || 2 | ACCÈS TABLE COMPLET | COUNT_TEST| 848K | | 3582 | 00:00:43 |----------------------------------------+--- --------------------------------+ Informations sur la projection de colonne (identifiées par l'ID d'opération) :------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Remarque----- - statistiques dynamiques utilisées :échantillonnage dynamique (niveau=2)19 lignes sélectionnées.SQL> 

En regardant le fichier de trace généré, Oracle utilise simplement count(*) tel quel pour renvoyer les résultats :

Requête finale après les transformations :******* LA REQUÊTE NON PARSÉE EST *******SELECT COUNT(*) "COUNT(*)" FROM "BING"."COUNT_TEST" "COUNT_TEST" ... ----- Expliquer le vidage de plan ---------- Table de plan -----============Table de plan============----------------------------------------+-------- ---------------------------+| identifiant | Opération | Nom | Lignes | Octets | Coût | Heure |----------------------------------------+------- ----------------------------+| 0 | SÉLECTIONNER L'INSCRIPTION | | | | 3582 | || 1 | TRIER L'AGRÉGAT | | 1 | | | || 2 | ACCÈS TABLE COMPLET | COUNT_TEST| 848K | | 3582 | 00:00:43 |----------------------------------------+--- --------------------------------+ Nom du bloc de requête / Alias ​​d'objet (identifié par l'ID d'opération) :---- -------------------------------------------------- ------1 - SEL$12 - SEL$1 / "COUNT_TEST"@"SEL$1"-------------------------------- --------------------------------Informations sur le prédicat :--------------- ---------SQL>

Pas de surprise là-bas; notez qu'Oracle n'étend pas le "*" à toutes les colonnes de la table - le "*" dans ce cas indique que toutes les lignes doivent être comptées. Si un nom de colonne réel avait été fourni, Oracle aurait compté les valeurs dans la colonne spécifiée. Voyons maintenant ce que fait Oracle avec une requête count(rowid) :

SQL> alter session set events ='10053 contexte de nom de trace pour toujours, niveau 2';Session modifiée.SQL> select count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> alter session set events ='10053 contexte de nom de trace désactivé';Session modifiée.SQL> expliquer le plan pour sélectionner count(rowid) from count_test;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection '));PLAN_TABLE_OUTPUT--------------------------------------------- -------------------------------------------------- ----Valeur de hachage du plan :371675025----------------------------------------+ -----------------------------------+| identifiant | Opération | Nom | Lignes | Octets | Coût | Heure |----------------------------------------+------- ----------------------------+| 0 | SÉLECTIONNER L'INSCRIPTION | | | | 3582 | || 1 | TRIER L'AGRÉGAT | | 1 | 12 | | || 2 | ACCÈS TABLE COMPLET | COUNT_TEST| 848K | 9941K | 3582 | 00:00:43 |----------------------------------------+--- --------------------------------+ Informations sur la projection de colonne (identifiées par l'ID d'opération) :------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Remarque----- - statistiques dynamiques utilisées :échantillonnage dynamique (niveau=2)19 lignes sélectionné.SQL>

Oracle génère une valeur rowid pour chaque ligne de la table, une opération qui consommera des ressources CPU. Étant donné que la requête a été renvoyée à peu près en même temps que la version count (*), le "coup" de performance semble être négligeable. L'ajout d'une clé primaire modifie légèrement les plans mais pas le texte de la requête :

SQL> modifier table count_test ajouter contrainte count_pk clé primaire(id);Table modifiée. SQL>SQL> alter session set events ='10053 contexte de nom de trace pour toujours, niveau 2';Session modifiée.SQL> select count(*) from count_test; COUNT(*)---------- 1000000SQL> alter session set events ='10053 trace name context off';Session modified.SQL> expliquer le plan pour sélectionner count(*) from count_test;Explained.SQL> select * de table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------- -------------------------------------------------- ---------------------Valeur de hachage du plan :371675025------------------------ -------------------------------------------------- | identifiant | Opération | Nom | Lignes | Coût (%CPU)| Heure |------------------------------------------------------------ --------------------------| 0 | SÉLECTIONNER L'INSCRIPTION | | 1 | 589 (2)| 00:00:01 || 1 | TRIER L'AGRÉGAT | | 1 | | || 2 | ANALYSE COMPLÈTE RAPIDE DE L'INDEX | COUNT_PK | 848K| 589 (2)| 00:00:01 |------------------------------------------------------- ------------------------------ Informations de projection de colonne (identifiées par l'ID d'opération) :---------- ------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Remarque----- - statistiques dynamiques utilisées :échantillonnage dynamique (niveau=2)19 lignes sélectionnées.SQL>SQL>SQL> alter session set events ='10053 contexte de nom de trace pour toujours, niveau 2'; Session modifiée.SQL> sélectionnez count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> alter session set events ='10053 contexte de nom de trace désactivé' ; Session modifiée. -------------------------------------------------- ---------------------------------------Valeur de hachage du plan :371675025------ -------------------------------------------------- --------------------------| identifiant | Opération | Nom | Lignes | Octets | Coût (%CPU)| Heure |------------------------------------------------------------ ----------------------------------| 0 | SÉLECTIONNER L'INSCRIPTION | | 1 | 12 | 589 (2)| 00:00:01 || 1 | TRIER L'AGRÉGAT | | 1 | 12 | | || 2 | ANALYSE COMPLÈTE RAPIDE DE L'INDEX | COUNT_PK | 848K| 9941K| 589 (2)| 00:00:01 |------------------------------------------------------- ------------------------------------------- Informations de projection de colonne (identifiées par l'ID d'opération) :-- -------------------------------------------------- ------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Remarque----- - statistiques dynamiques utilisées :échantillonnage dynamique (niveau =2)19 lignes sélectionnées.SQL>SQL> spool offcommit ;

Les détails de la trace 10053 n'ont pas changé après l'ajout de la clé primaire.

Il semblerait que deux informations aient été glanées à partir de cette expérience - count(rowid) n'est pas meilleur que count(*) lorsque les tables contiennent des colonnes CLOB et que count(*) ne développe pas la liste des colonnes comme le fait "select *" (et il n'y a aucune raison de croire que ce devrait être le cas).

La preuve est dans le pudding, comme le dit le vieil adage.

# # #

Voir les articles de David Fitzjarrell