Oracleでは、クエリに気づきました
SELECT COUNT(*) FROM sometable;
大きなテーブルでは非常に遅いです。データベースのように、実際にはすべての行を通過し、一度に1つずつカウンターを増やします。テーブルのどこに行がいくつあるか、テーブルのどこかにカウンターがあると思います。
Oracleのテーブルの行数を確認したい場合、これを行う最も速い方法は何ですか?
考えてみてください。データベースは実際にすべての行に行かなければなりません。 マルチユーザー環境では、COUNT(*)
はCOUNT(*)
と異なる可能性があります。セッションごとに異なるカウンターを使用するのは実用的ではないため、文字通り行をカウントする必要があります。ほとんどの場合、とにかくクエリにWHERE句またはJOINが含まれているため、仮想カウンターはかなり実用的です。
ただし、物事をスピードアップする方法があります。NOTNULL列にINDEXがある場合、Oracleはテーブルではなくインデックスの行をカウントします。適切なリレーショナルモデルでは、すべてのテーブルにプライマリキーがあるため、COUNT(*)
はプライマリキーのインデックスを使用します。
ビットマップインデックスにはNULL行のエントリがあるため、COUNT(*)はビットマップインデックスが使用可能な場合はそれを使用します。
おおよその概算が必要な場合は、サンプルから推定できます。
SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);
速度を上げる(ただし精度を下げる)には、サンプルサイズを小さくできます。
SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);
さらに高速にするには(ただし精度はさらに低下します)、ブロックごとのサンプリングを使用できます。
SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);
これは大きなテーブルに最適です。
SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';
小規模から中規模のテーブルの場合、次のものは問題ありません。
SELECT COUNT(Primary_Key) FROM table_name;
乾杯、
テーブルのインデックスがNOT NULL列にある場合、COUNT(*)はそれを使用します。それ以外の場合は、全表スキャンが実行されます。インデックスは一意である必要はなく、NOT NULLである必要があることに注意してください。
これが表です...
SQL> desc big23
Name Null? Type
----------------------------------------- -------- ---------------------------
PK_COL NOT NULL NUMBER
COL_1 VARCHAR2(30)
COL_2 VARCHAR2(30)
COL_3 NUMBER
COL_4 DATE
COL_5 NUMBER
NAME VARCHAR2(10)
SQL>
まず、インデックスなしでカウントを行います....
SQL> explain plan for
2 select count(*) from big23
3 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
select * from table)dbms_xplan.display)
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
Plan hash value: 983596667
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1618 (1)| 00:00:20 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| BIG23 | 472K| 1618 (1)| 00:00:20 |
--------------------------------------------------------------------
Note
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
- dynamic sampling used for this statement
13 rows selected.
SQL>
いいえ、NULLエントリを含むことができる列にインデックスを作成します...
SQL> create index i23 on big23(col_5)
2 /
Index created.
SQL> delete from plan_table
2 /
3 rows deleted.
SQL> explain plan for
2 select count(*) from big23
3 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
Plan hash value: 983596667
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1618 (1)| 00:00:20 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| BIG23 | 472K| 1618 (1)| 00:00:20 |
--------------------------------------------------------------------
Note
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
- dynamic sampling used for this statement
13 rows selected.
SQL>
最後に、NOT NULL列にインデックスを作成しましょう....
SQL> drop index i23
2 /
Index dropped.
SQL> create index i23 on big23(pk_col)
2 /
Index created.
SQL> delete from plan_table
2 /
3 rows deleted.
SQL> explain plan for
2 select count(*) from big23
3 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------
Plan hash value: 1352920814
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 326 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| I23 | 472K| 326 (1)| 00:00:04 |
----------------------------------------------------------------------
Note
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
- dynamic sampling used for this statement
13 rows selected.
SQL>
オプション1:スキャンに使用できるNULL以外の列にインデックスを設定します。または、関数ベースのインデックスを次のように作成します。
create index idx on t(0);
これをスキャンして、カウントを確認できます。
オプション2:監視を有効にしている場合、監視ビューUSER_TAB_MODIFICATIONSを確認し、関連する値をテーブル統計に追加/減算します。
オプション3:大規模なテーブルをすばやく見積もるには、SAMPLE句を呼び出します...たとえば...
SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1);
オプション4:マテリアライズドビューを使用して、count(*)を維持します。しかし強力な薬。
ええと...
高速リフレッシュマテリアライズドビューを作成して、カウントを保存できます。
例:
create table sometable (
id number(10) not null primary key
, name varchar2(100) not null);
create materialized view log on sometable with rowid including new values;
create materialized view sometable_count
refresh on commit
as
select count(*) count
from sometable;
insert into sometable values (1,'Raymond');
insert into sometable values (2,'Hans');
commit;
select count from sometable_count;
テーブルの突然変異は少し遅くなりますが、カウントはずっと速くなります。
テーブルのカウントを取得する最も速い方法は、まさにあなたがしたことです。 Oracleがまだ知らないトリックはありません。
あなたが私たちに言っていないものがあります。つまり、なぜこれをもっと速くすべきだと思いますか?
例えば:
私は41秒で満足できないだろうと認めますが、本当になぜあなたはそれがより速いはずだと思いますか?テーブルに180億行あり、2001年にガレージセールで購入したラップトップで実行されていると言えば、41秒は、より良いハードウェアを入手しない限り、おそらく「良い」とは言えません。ただし、Oracle 9を使用しており、昨年の夏に統計を実行したと言った場合、おそらく別の提案が得られるでしょう。
2016年4月に公開された Ask Tom から関連する回答がありました。
サーバーに十分な能力がある場合は、次のことができます
select /*+ parallel */ count(*) from sometable
近似値の直後であれば、次のことができます。
select 5 * count(*) from sometable sample block (10);
また、ある場合
- nULLを含まないが、NOT NULLとして定義されていない列
- その列にインデックスがあります
あなたが試すことができます:
select /*+ index_ffs(t) */ count(*) from sometable t where indexed_col is not null
次の方法を使用すると、パフォーマンスを向上できます。
SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name
FROM table_name
WHERE column_name = 'xxxxx' AND ROWNUM = 1);