sql >> データベース >  >> RDS >> Oracle

COUNT(rowid)はCOUNT(*)よりも高速ですか?

    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の記事を見る


    1. MicrosoftAccessのナビゲーションボタンからマクロを実行する方法

    2. SQLintersectionのためにラスベガスに参加して$100を節約

    3. Oracle異種サービスでのMySQLの使用

    4. postgres:ユーザーをスーパーユーザーにアップグレードしますか?