LoginSignup
2
1

More than 5 years have passed since last update.

巨大テーブルのデータ件数を取得する

Posted at

目的

超巨大なテーブルの件数をサクッと知りたい!
最近数百億〜兆レコードを持つテーブルを触る機会がありました。
SELECT COUNT(1)しても10分以上かかる!ということが頻繁にあったため、簡単に件数を知る方法を調べていたら行き着きました。
それが「DBA_TAB_MODIFICATIONS」ディクショナリです。

DBA_TAB_MODIFICATIONSとは

表の統計情報を収集した時点から現時点までの、表に対するINSERT/UPDATE/DELETE/TRUNCATEがどの程度行われたかを保持するデータ・ディクショナリです。

SQL> desc dba_tab_modifications 
 名前                NULL?    型
 ------------------ -------- ------------------------------------------
 TABLE_OWNER                 VARCHAR2(30)    #←スキーマ名
 TABLE_NAME                  VARCHAR2(30)    #←テーブル名
 PARTITION_NAME              VARCHAR2(30)    #←パーティション名
 SUBPARTITION_NAME           VARCHAR2(30)    #←サブパーティション名
 INSERTS                     NUMBER          #←最後に統計が取られた以降のINSERTレコード数
 UPDATES                     NUMBER          #←最後に統計が取られた以降のUPDATEレコード数
 DELETES                     NUMBER          #←最後に統計が取られた以降のDELETEレコード数
 TIMESTAMP                   DATE            #←最後に変更された日時
 TRUNCATED                   VARCHAR2(3)     #←最後に統計が取られた以降にTRUNCATEされたか(Yes/No)
 DROP_SEGMENTS               NUMBER          #←最後に統計が取られた以降にDROPされたセグメント数

実際にDBA_TAB_MODOFICATIONSがどう変わっていくかを見てみます

SQL> show user                                
ユーザーは"TEST_USER"です。

SQL> -- テーブルを作成する
SQL> create table test_table(id number(10), name varchar2(100));
表が作成されました。

SQL> -- テーブルのディクショナリ情報を見てみる(統計が取られていないことを確認)
SQL> select owner, table_name, num_rows, last_analyzed from dba_tables where owner = 'TEST_USER' and table_name = 'TEST_TABLE';

OWNER      TABLE_NAME      NUM_ROWS LAST_ANALYZED
---------- ------------- ---------- -------------------
TEST_USER  TEST_TABLE         

SQL> -- DBA_TAB_MODIFICATIONSを見てみる
SQL> -- 統計も取られておらず、データのDMLも行われていないため、レコードが存在しない
SQL> select count(1) from dba_tab_modifications where table_owner = 'TEST_USER' and table_name = 'TEST_TABLE';

  COUNT(1)
----------
         0

SQL> --1レコードINSERTする
SQL> insert into test_table values(1, 'Mike');

1行が作成されました。

SQL> commit;

SQL> --再度DBA_TAB_MODIFICATIONSを見てみるが0件
SQL> select count(1) from dba_tab_modifications where table_owner = 'TEST_USER' and table_name = 'TEST_TABLE';

  COUNT(1)
----------
         0

SQL> --Oracleのメモリに残っている情報をDBA_TAB_MODIFICATIONSに書き出す
SQL> exec DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;

PL/SQLプロシージャが正常に完了しました。

SQL> select count(1) from dba_tab_modifications where table_owner = 'TEST_USER' and table_name = 'TEST_TABLE';

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

SQL> --insertが1件となっている
SQL> select table_owner, table_name, inserts, updates, deletes, truncated, timestamp
  2  from dba_tab_modifications
  3  where table_owner = 'TEST_USER' and table_name = 'TEST_TABLE';

TABLE_OWNER  TABLE_NAME    INSERTS    UPDATES    DELETES    TRU TIMESTAMP
------------ ------------- ---------- ---------- ---------- --- -------------------
TEST_USER    TEST_TABLE             1          0          0 NO 
  2019/03/04 12:24:22

SQL> --統計情報をとる
SQL> exec DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST_USER', tabname => 'TEST_TABLE', cascade => FALSE);

PL/SQLプロシージャが正常に完了しました。

SQL> 統計情報が取られた後にDMLを実行していないためレコードがない
SQL> select table_owner, table_name, inserts, updates, deletes, truncated, timestamp
  2  from dba_tab_modifications
  3  where table_owner = 'TEST_USER' and table_name = 'TEST_TABLE';

レコードが選択されませんでした。

注意点

DBA_TAB_MODIFICATIONSの情報は常に更新されているわけではないため、精度の高い情報が欲しい場合には、直前にDBMS_STATS.FLUSH_DATABASE_MONITORING_INFOを実行する必要があります。
近似値程度でもよければ実行する必要はありません。

便利な使い方

データウェアハウスのような超巨大なテーブルのデータ件数を知りたい、でもSELECT COUNT(1) 〜ではとてもではないけど時間がかかりすぎる場合、DBA_TAB_MODIFICATIONSとDBA_TABLESを使うことでデータ件数をサクッと知ることができます。

例えば、368億件のレコードを持つテーブルに対して件数を取得する場合、

  • SELECT COUNT(1)〜だと12分以上
  • DBA_TAB_MODIFICATIONSとDBA_TABLESを使うと2秒

と非常に効率が良いことがわかります。

SQL> --テーブルの件数取得だけで12分かかる
SQL> select  /*+ PARALLEL(6) */ count(1) from TEST_SCHEMA.LARGE_TABLE;

     COUNT(1)
-------------
  36803211715

経過: 00:12:04.73
SQL> 
SQL> select t.owner, t.table_name, t.num_rows, m.inserts, m.updates, m.deletes, m.truncated, t.num_rows+m.inserts-m.deletes as num_rows_real
  2  from dba_tables t
  3  left outer join dba_tab_modifications m
  4  on   t.owner = m.table_owner
  5  and  t.table_name = m.table_name
  6  where t.owner = 'TEST_SCHEMA'
  7  and   t.table_name = 'LARGE_TABLE';

OWNER            TABLE_NAME              NUM_ROWS    INSERTS    UPDATES    DELETES TRU NUM_ROWS_REAL
---------------- ------------------ ------------- ---------- ---------- ---------- --- -------------
TEST_SCHEMA      LARGE_TABLE          35602049044 2387593647          0 1186430976 NO    36803211715

経過: 00:00:02.49
SQL> 
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1