Oracleフォーラムやニュースグループでの長年の議論は、count(*)を使用して特定のテーブルから行数を返す効率性でした。その議論の新しいしわは、より効率的な代替手段としてcount(rowid)を導入しています。引数は、count(*)が「select *…」のように列リスト全体を展開することを示しています。そのため、CLOB列が目的のテーブルに存在する場合、リソースシンクになる可能性があります。その議論を見て、それが水を保持しているかどうかを見てみましょう。まず、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, 'Record '||z, 'Clob value '||z); 5 end loop; 6 7 commit; 8 end; 9 / PL/SQL procedure successfully completed. SQL>
次に、イベント10053を設定してオプティマイザトレースをダンプし、Oracleがcount()クエリの実行をどのように計画しているかを確認します。
SQL> alter session set events = '10053 trace name context forever, level 2'; Session altered.
ステージが設定されました。count()のいくつかのバリアントを実行して、Oracleの動作を確認しましょう。まず、ストレートカウント(*)を実行し、計画を表示します:
SQL> select count(*) from count_test; COUNT(*) ---------- 1000000 SQL> alter session set events = '10053 trace name context off'; Session altered. SQL> explain plan for select count(*) from count_test; Explained. SQL> select * from table(dbms_xplan.display(null,null,'projection')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------- Plan hash value: 371675025 ----------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 3582 | | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | TABLE ACCESS FULL | COUNT_TEST| 848K | | 3582 | 00:00:43 | ----------------------------------------+-----------------------------------+ Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019) Note ----- - dynamic statistics used: dynamic sampling (level=2) 19 rows selected. SQL>
生成されたトレースファイルを見ると、Oracleは単にcount(*)をそのまま使用して結果を返します。
Final query after transformations:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "BING"."COUNT_TEST" "COUNT_TEST" ... ----- Explain Plan Dump ----- ----- Plan Table ----- ============ Plan Table ============ ----------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 3582 | | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | TABLE ACCESS FULL | COUNT_TEST| 848K | | 3582 | 00:00:43 | ----------------------------------------+-----------------------------------+ Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------ 1 - SEL$1 2 - SEL$1 / "COUNT_TEST"@"SEL$1" ------------------------------------------------------------ Predicate Information: ------------------------ SQL>
そこには驚きはありません。 Oracleは「*」をテーブル内のすべての列に展開しないことに注意してください。この場合の「*」は、すべての行がカウントされることを示します。実際の列名が指定されている場合、Oracleは指定された列の値をカウントします。次に、Oracleがcount(rowid)クエリで何をするかを見てみましょう。
SQL> alter session set events = '10053 trace name context forever, level 2'; Session altered. SQL> select count(rowid) from count_test; COUNT(ROWID) ------------ 1000000 SQL> 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 --------------------------------------------------------------------------------------------------- Plan hash value: 371675025 ----------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 3582 | | | 1 | SORT AGGREGATE | | 1 | 12 | | | | 2 | TABLE ACCESS FULL | COUNT_TEST| 848K | 9941K | 3582 | 00:00:43 | ----------------------------------------+-----------------------------------+ Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10] Note ----- - dynamic statistics used: dynamic sampling (level=2) 19 rows selected. SQL>
Oracleは、テーブル内の各行に対してROWID値を生成します。これは、一部のCPUリソースを消費する操作です。クエリはcount(*)バージョンとほぼ同時に返されるため、パフォーマンスの「ヒット」はごくわずかであるように見えます。主キーを追加すると、プランはわずかに変更されますが、クエリテキストは変更されません:
SQL> alter table count_test add constraint count_pk primary key(id); Table altered. SQL> SQL> alter session set events = '10053 trace name context forever, level 2'; Session altered. SQL> select count(*) from count_test; COUNT(*) ---------- 1000000 SQL> alter session set events = '10053 trace name context off'; Session altered. SQL> explain plan for select count(*) from count_test; Explained. SQL> select * from table(dbms_xplan.display(null,null,'projection')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------ Plan hash value: 371675025 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 589 (2)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| COUNT_PK | 848K| 589 (2)| 00:00:01 | -------------------------------------------------------------------------- Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (rowset=1019) Note ----- - dynamic statistics used: dynamic sampling (level=2) 19 rows selected. SQL> SQL> SQL> alter session set events = '10053 trace name context forever, level 2'; Session altered. SQL> select count(rowid) from count_test; COUNT(ROWID) ------------ 1000000 SQL> 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 ------------------------------------------------------------------------------------------ Plan hash value: 371675025 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 12 | 589 (2)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 12 | | | | 2 | INDEX FAST FULL SCAN| COUNT_PK | 848K| 9941K| 589 (2)| 00:00:01 | ---------------------------------------------------------------------------------- Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (rowset=256) ROWID[ROWID,10] Note ----- - dynamic statistics used: dynamic sampling (level=2) 19 rows selected. SQL> SQL> spool off commit;
主キーが追加された後、10053トレースの詳細は変更されませんでした。
この実験から2つの情報が収集されたように見えます。テーブルにCLOB列が含まれ、count(*)が「select *」のように列リストを展開しない場合、count(rowid)はcount(*)よりも優れていません。 (そしてそうすべきだと信じる理由はありません。)
古い格言が行くように、証拠はプリンにあります。
###
David Fitzjarrellの記事を見る