web-dev-qa-db-ja.com

SELECT COUNT(*)FROM sometableに対するOracleのより高速な代替手段

Oracleでは、クエリに気づきました

SELECT COUNT(*) FROM sometable;

大きなテーブルでは非常に遅いです。データベースのように、実際にはすべての行を通過し、一度に1つずつカウンターを増やします。テーブルのどこに行がいくつあるか、テーブルのどこかにカウンターがあると思います。

Oracleのテーブルの行数を確認したい場合、これを行う最も速い方法は何ですか?

58
Eli Courtwright

考えてみてください。データベースは実際にすべての行に行かなければなりません。 マルチユーザー環境では、COUNT(*)COUNT(*)と異なる可能性があります。セッションごとに異なるカウンターを使用するのは実用的ではないため、文字通り行をカウントする必要があります。ほとんどの場合、とにかくクエリにWHERE句またはJOINが含まれているため、仮想カウンターはかなり実用的です。

ただし、物事をスピードアップする方法があります。NOTNULL列にINDEXがある場合、Oracleはテーブルではなくインデックスの行をカウントします。適切なリレーショナルモデルでは、すべてのテーブルにプライマリキーがあるため、COUNT(*)はプライマリキーのインデックスを使用します。

ビットマップインデックスにはNULL行のエントリがあるため、COUNT(*)はビットマップインデックスが使用可能な場合はそれを使用します。

28
Vincent Malgrat

おおよその概算が必要な場合は、サンプルから推定できます。

SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);

速度を上げる(ただし精度を下げる)には、サンプルサイズを小さくできます。

SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);

さらに高速にするには(ただし精度はさらに低下します)、ブロックごとのサンプリングを使用できます。

SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);

55
Jeffrey Kemp

これは大きなテーブルに最適です。

SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';

小規模から中規模のテーブルの場合、次のものは問題ありません。

SELECT COUNT(Primary_Key) FROM table_name;

乾杯、

46
AMISH G SHAH

テーブルのインデックスが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>
13
APC

オプション1:スキャンに使用できるNULL以外の列にインデックスを設定します。または、関数ベースのインデックスを次のように作成します。

create index idx on t(0);

これをスキャンして、カウントを確認できます。

オプション2:監視を有効にしている場合、監視ビューUSER_TAB_MODIFICATIONSを確認し、関連する値をテーブル統計に追加/減算します。

オプション3:大規模なテーブルをすばやく見積もるには、SAMPLE句を呼び出します...たとえば...

SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1); 

オプション4:マテリアライズドビューを使用して、count(*)を維持します。しかし強力な薬。

ええと...

7
David Aldridge

高速リフレッシュマテリアライズドビューを作成して、カウントを保存できます。

例:

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; 

テーブルの突然変異は少し遅くなりますが、カウントはずっと速くなります。

5
tuinstoel

テーブルのカウントを取得する最も速い方法は、まさにあなたがしたことです。 Oracleがまだ知らないトリックはありません。

あなたが私たちに言っていないものがあります。つまり、なぜこれをもっと速くすべきだと思いますか?

例えば:

  1. 少なくとも、Oracleが何をしているかを確認するための説明計画を作成しましたか?
  2. このテーブルには何行ありますか?
  3. 使用しているOracleのバージョンは何ですか? 8,9,10,11 ... 7?
  4. このテーブルでデータベース統計を実行したことがありますか?
  5. これは頻繁に更新されるテーブルまたはバッチのロードですか、それとも単なる静的データですか?
  6. これはあなたが持っている唯一の遅いCOUNT(*)ですか?
  7. SELECT COUNT(*)FROM Dualはどれくらいかかりますか?

私は41秒で満足できないだろうと認めますが、本当になぜあなたはそれがより速いはずだと思いますか?テーブルに180億行あり、2001年にガレージセールで購入したラップトップで実行されていると言えば、41秒は、より良いハードウェアを入手しない限り、おそらく「良い」とは言えません。ただし、Oracle 9を使用しており、昨年の夏に統計を実行したと言った場合、おそらく別の提案が得られるでしょう。

3
David

2016年4月に公開された Ask Tom から関連する回答がありました。

サーバーに十分な能力がある場合は、次のことができます

select /*+ parallel */ count(*) from sometable

近似値の直後であれば、次のことができます。

select 5 * count(*) from sometable sample block (10);

また、ある場合

  1. nULLを含まないが、NOT NULLとして定義されていない列
  2. その列にインデックスがあります

あなたが試すことができます:

select /*+ index_ffs(t) */ count(*) from sometable  t where indexed_col is not null
2
m.r226

次の方法を使用すると、パフォーマンスを向上できます。

SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name 
FROM table_name 
WHERE column_name = 'xxxxx' AND ROWNUM = 1);