Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Ist COUNT(rowid) schneller als COUNT(*)?

Eine langjährige Diskussion in Oracle-Foren und -Newsgroups war die Effizienz der Verwendung von count(*), um eine Zeilenanzahl aus einer bestimmten Tabelle zurückzugeben. Eine neue Falte in dieser Diskussion führt nun count(rowid) als effizientere Alternative ein; Das Argument besagt, dass count(*) die gesamte Spaltenliste erweitert, ähnlich wie „select * …“, und als solche eine Ressourcensenke sein könnte, wenn CLOB-Spalten in der gewünschten Tabelle vorhanden sind. Schauen wir uns dieses Argument an und sehen, ob es stichhaltig ist. Beginnen wir mit dem Erstellen und Füllen einer Tabelle, die eine CLOB-Spalte enthält:

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, 'Datensatz'||z, 'Clob-Wert'||z); 5 Endschleife; 6 7 festschreiben; 8 Ende; 9 /PL/SQL-Prozedur erfolgreich abgeschlossen.SQL>

Als Nächstes legen wir Ereignis 10053 fest, um den Optimierer-Trace auszugeben, damit wir sehen können, wie Oracle plant, die count()-Abfragen auszuführen:

SQL> alter session set events ='10053 Ablaufverfolgungsnamenkontext für immer, Ebene 2';Sitzung geändert.

Die Bühne ist bereitet, lassen Sie uns einige Varianten von count() ausführen, um zu sehen, wie sich Oracle verhält. Zuerst führen wir eine direkte Zählung (*) durch und zeigen den Plan an:

SQL> select count(*) from count_test; COUNT(*)---------- 1000000SQL> Alter session set events ='10053 Trace Name Context Off';Session geändert.SQL> Plan für select count(*) from count_test erklären;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------- -------------------------------------------------- -------------------------Planhashwert:371675025-------------------- --------------------+----------------------------- ------+| ID | Betrieb | Name | Zeilen | Bytes | Kosten | Uhrzeit |----------------------------------------+------- ----------------------------+| 0 | AUSSAGE AUSWÄHLEN | | | | 3582 | || 1 | AGGREGAT SORTIEREN | | 1 | | | || 2 | TABELLE ZUGRIFF VOLL | COUNT_TEST| 848K | | 3582 | 00:00:43 |----------------------------------------+--- --------------------------------+ Spaltenprojektionsinformationen (identifiziert durch Vorgangs-ID):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Hinweis----- - dynamische Statistik verwendet:dynamisches Sampling (Level=2)19 Zeilen ausgewählt.SQL> 

Betrachtet man die generierte Ablaufverfolgungsdatei, verwendet Oracle einfach count(*) unverändert, um die Ergebnisse zurückzugeben:

Abschließende Abfrage nach Transformationen:******* UNPARSED QUERY IS ********SELECT COUNT(*) "COUNT(*)" FROM "BING".."COUNT_TEST" "COUNT_TEST" ... ----- Plan-Dump erklären ---------- Plan-Tabelle -----============Plan-Tabelle============-----------------------------------+-------- ----------------------------+| ID | Betrieb | Name | Zeilen | Bytes | Kosten | Uhrzeit |----------------------------------------+------- ----------------------------+| 0 | AUSSAGE AUSWÄHLEN | | | | 3582 | || 1 | AGGREGAT SORTIEREN | | 1 | | | || 2 | TABELLE ZUGRIFF VOLL | COUNT_TEST| 848K | | 3582 | 00:00:43 |----------------------------------------+--- -------------------------------+ Abfrageblockname/Objektalias (identifiziert durch Vorgangs-ID):---- -------------------------------------------------- ------1 - SEL$12 - SEL$1 / "COUNT_TEST"@"SEL$1"---------------------- ---------------------------------Prädikatsinformationen:--------------- ---------SQL>

Da gibt es keine Überraschungen; Beachten Sie, dass Oracle das „*“ nicht auf alle Spalten in der Tabelle erweitert – das „*“ gibt in diesem Fall an, dass alle Zeilen gezählt werden sollen. Wäre ein tatsächlicher Spaltenname angegeben worden, hätte Oracle Werte in der angegebenen Spalte gezählt. Sehen wir uns nun an, was Oracle mit einer count(rowid)-Abfrage macht:

SQL> alter session set events ='10053 Trace-Namenskontext für immer, Ebene 2';Sitzung geändert.SQL> select count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> alter session set events ='10053 trace name context off';Session altered.SQL> Explain plan for select count(rowid) from count_test;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection '));PLAN_TABLE_OUTPUT--------------------------------------------- -------------------------------------------------- ----Planhashwert:371675025----------------------------------------+ -----------------------------------+| ID | Betrieb | Name | Zeilen | Bytes | Kosten | Uhrzeit |----------------------------------------+------- ----------------------------+| 0 | AUSSAGE AUSWÄHLEN | | | | 3582 | || 1 | AGGREGAT SORTIEREN | | 1 | 12 | | || 2 | TABELLE ZUGRIFF VOLL | COUNT_TEST| 848K | 9941K | 3582 | 00:00:43 |----------------------------------------+--- --------------------------------+ Spaltenprojektionsinformationen (identifiziert durch Vorgangs-ID):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Hinweis----- - dynamische Statistik verwendet:dynamisches Sampling (Level=2)19 Zeilen selected.SQL>

Oracle generiert einen Rowid-Wert für jede Zeile in der Tabelle, eine Operation, die einige CPU-Ressourcen verbraucht. Da die Abfrage ungefähr zur gleichen Zeit wie die count(*)-Version zurückgegeben wurde, scheint der „Treffer“ der Leistung vernachlässigbar zu sein. Das Hinzufügen eines Primärschlüssels ändert die Pläne geringfügig, aber nicht den Abfragetext:

SQL> alter table count_test Einschränkung hinzufügen count_pk Primärschlüssel (ID); Tabelle geändert. SQL> SQL> alter session set events ='10053 Ablaufverfolgungsnamenkontext für immer, Ebene 2'; Sitzung geändert. SQL> select count(*) from count_test; COUNT(*)---------- 1000000SQL> Alter session set events ='10053 Trace Name Context Off';Session geändert.SQL> Plan für select count(*) from count_test erklären;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------- -------------------------------------------------- ---------------------Plan-Hashwert:371675025------------------------ -------------------------------------------------- | ID | Betrieb | Name | Zeilen | Kosten (%CPU)| Zeit |------------------------------------------------------------ --------------------------| 0 | AUSSAGE AUSWÄHLEN | | 1 | 589 (2)| 00:00:01 || 1 | AGGREGAT SORTIEREN | | 1 | | || 2 | INDEX SCHNELLER VOLLSTÄNDIGER SCAN| COUNT_PK | 848K| 589 (2)| 00:00:01 |--------------------------------------------------- ------------------------------ Spaltenprojektionsinformationen (identifiziert durch Vorgangs-ID):---------- ------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019)Hinweis ----- - dynamische Statistik verwendet:dynamisches Sampling (Level=2)19 Zeilen ausgewählt.SQL>SQL>SQL> ändern session set events ='10053 Trace-Namenskontext für immer, Ebene 2'; Sitzung geändert.SQL> select count(rowid) from count_test;COUNT(ROWID)------------ 1000000SQL> Sitzungssatzereignisse ändern ='10053 Trace-Namenskontext aus'; Sitzung geändert.SQL> Plan für select count(rowid) from count_test erklären;Explained.SQL> select * from table(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT- -------------------------------------------------- ---------------------------------------Planhashwert:371675025------ -------------------------------------------------- --------------------------| ID | Betrieb | Name | Zeilen | Bytes | Kosten (%CPU)| Zeit |------------------------------------------------------------ ----------------------------------| 0 | AUSSAGE AUSWÄHLEN | | 1 | 12 | 589 (2)| 00:00:01 || 1 | AGGREGAT SORTIEREN | | 1 | 12 | | || 2 | INDEX SCHNELLER VOLLSTÄNDIGER SCAN| COUNT_PK | 848K| 9941K| 589 (2)| 00:00:01 |--------------------------------------------------- -------------------------------------- Informationen zur Spaltenprojektion (identifiziert durch Vorgangs-ID):-- -------------------------------------------------- ------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10]Hinweis----- - Verwendete dynamische Statistiken:dynamisches Sampling (level =2)19 Zeilen ausgewählt.SQL>SQL>spool offcommit;

Die Ablaufverfolgungsdetails 10053 haben sich nicht geändert, nachdem der Primärschlüssel hinzugefügt wurde.

Anscheinend wurden aus diesem Experiment zwei Informationen gewonnen – count(rowid) ist nicht besser als count(*), wenn Tabellen CLOB-Spalten enthalten, und diese count(*) erweitert die Spaltenliste nicht wie „select *“. (und es gibt keinen Grund zu der Annahme, dass dies der Fall sein sollte).

Der Beweis liegt im Pudding, wie das alte Sprichwort sagt.

# # #

Siehe Artikel von David Fitzjarrell