DHJJ [Hatsune's Journal Japan] blog

Hatsune's Journal Japan blog

目次

Blog 利用状況

ニュース

最新ツイート

運営サイト

Hatsune's Journal Japan
DHJJ

著作など

資格など

OCP
MCP

書庫

日記カテゴリ

わんくま同盟

1000件より多くレコードがあるかどうか判定する方法

SELECT COUNT(*) FROM tableA WHERE rownum <= 1001

1000件より多いかどうか判定するには、1200万件の実データを使って実測した結果、単純にCOUNT(*)した結果で判断するのではなく、1001件で足切りしたレコードの件数が1001かどうかで判定するのが良い事が分かりました。

SQL> SELECT COUNT(*) FROM tableA WHERE rownum <= 1001
1001
COST=12215
SELECT STATEMENT
  SORT AGGREGATE
    COUNT STOPKEY
      INDEX FAST FULL SCAN
PHYSICAL READS 17

キャッシュされていないときは1秒未満。2回目以降はすべてキャッシュに入っているのでPHYSICAL READSは0、実行時間はほぼ瞬時。

SQL> SELECT COUNT(*) FROM tableA
12,000,000

COST=12215
SELECT STATEMENT
  SORT AGGREGATE
    INDEX FAST FULL SCAN
PHYSICAL READS 54089

実行時間は30~35秒。何度やってもその範囲。

[追記]

Oracle Database 10g Enterprise Edition Release 10.2.0.3.0

投稿日時 : 2008年5月27日 11:35

Feedback

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 13:03 こあら

愚かしい突っ込みかもしれませんが・・・
12,000,000件のSELECTにWHERE句が付いているのは間違いですよね?

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 13:56 はつね

うん、間違いですね~。削っておきます。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 14:36 黒龍

CountはDISTINCTつけたほうが早いですよ。理由は忘れましたがw

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 16:12 はつね

> CountはDISTINCTつけたほうが早い

変わらなかったよ。
SELECT DISTINCT COUNT(*) FROM tableA

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 16:37 癒耶

SELECT DISTINCT COUNT(*) FROM tableA
ではなく
SELECT COUNT(DISTINCT Name) FROM tableA
(Nameはインデックスのついているもので主キー?)
みたいな感じではないかと。

*よりも項目名の方が早い

項目名ならDISTINCT入れると更に早い

という流れだった気がします。

全部が全部早くなるかはわかりませんが・・・
あと主キーが複数ある場合には使えませんね。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 17:37 こあら

ということは、

select count(1) from tableA

が速い(PHYSICAL READSが一番少ないので)というのも本当っぽいですね。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 19:52 はつね

> 癒耶さん

でも、PKが1つだけだったらDISTINCTつける意味ってあるのでしょうか。これまた最適化されちゃってつけていないときと同じになるような気がします。
まあ、そもそもOracleで言えば、項目名指定するよりも*指定した方がレコード数カウントするのは早いので「*よりも項目名の方が早い」ということ自体が今では成立しない感じですね。

> こあらさん

その推論の流れが良く分からないです。
なぜ、それだとPHYSICAL READSが一番少ないないのでしょうか。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 21:25 癒耶

SQLServerで簡単な試験データを作って動かしてみました。
[ 1 ]
SELECT COUNT(*)
FROM 試験用TBL
結果:処理時間は 460ミリ秒でした。

[ 2 ]
SELECT COUNT(試験.KEY項目)
FROM 試験用TBL AS 試験
結果:処理時間は 160ミリ秒でした。

[ 3 ]
SELECT COUNT(DISTINCT 名寄.KEY項目)
FROM 試験用TBL AS 名寄
結果:処理時間は 1410ミリ秒でした。

Σ('ω'*)!?
というわけでSQLServerでは項目名指定の方が早いみたいでした。
インデックスで数えるから早くなるみたいですね。
通常の*だとインデックスを使わない分遅くなるのだとか。
DISTINCTを使ったら圧倒的に遅くなったので
これはSQLServerの場合には使えないのかな?
何回か実行してみたけど、大体こんな感じでした。
Oracleではインデックスの扱いはどうなるのかな?
*でもインデックスの有無を見るようなら変わらなそう?

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 21:30 はつね

> 癒耶さん
テストありがとうございます。
てか、がんばれ!SQL Serverのオプティマイザ。
たぶん、クラスタ化インデックスだとかなんか色々絡みそうな予感。
DISTINCTは重複チェックする分だけ時間かかるのが普通だと思うんだよね、想像するに。

Oracleについては、*指定したときに一番最適なインデックスでCOUNTする事に仕様上なっているので、*でFAだと思う。

※名寄という名称にニヤリとした俺ガイル

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 21:33 癒耶

ごめんなさい。
別名が一部おかしいですが気にしないでください。
レスを見ていてこちらも試してみたくなったので・・・
[ 4 ]
SELECT COUNT(1)
FROM 試験用TBL
結果:処理時間は 160ミリ秒でした。

これも2番と同じかそれ以上の速度という結果に。
中身に関してどれだけ触れずに入られるかで
大分変わってくるという事ですね。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 21:40 癒耶

>はつね様
書き込みをしている間に・・・
人ん家の庭で何やってるんだというツッコミがきそうな予感がしました。
申し訳ありません。
SQLServerのオプチマイザは
ツッコミどころいっぱいですよね。
Oracleは触った事が無かったのですが
素晴らしい仕様ですね。

※名寄に気づいた時には手遅れでした。恥ずかしいので見なかったことに・・・orz

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 21:49 こあら

>> select count(1) from tableA
>> が速い(PHYSICAL READSが一番少ないので)というのも本当っぽいですね。

> その推論の流れが良く分からないです。
> なぜ、それだとPHYSICAL READSが一番少ないのでしょうか。

select count(*) from tableA
=> full scan

select count(PK) from tableA
=> index scan

select count(1) from tableA
=> 何スキャンと言うか不明(rowidへのアクセスのみ)

という流れです。

COUNT(*)=テーブル全体

COUNT(PK)=インデックスのみ

COUNT(1)=rowidのみ

とPHYSICAL READSが減っているのではないか?という推論です。

※ただし最近の OracleCBO では COUNT(*) も index scan になるような情報も見かけましたのでこの限りでは無いかもしれません。



それと上記前提を踏まえてCOUNT(*)がCOUNT(PK)より速いとなると、
indexのみへのアクセス以外の特殊な最適化が働いている可能性が高いですね。
# 例えば select count(*) from table のときは、
# リーフの終端アドレス ― リーフの開始アドレス = レコード数とみなす
# のような。

ちょっとまとまりのない書き込みですみません。。。

# re: 1000件より多くレコードがあるかどうか判定する方法 2008/05/27 22:45 はつね

> 癒耶さん
いえいえ、このエントリがみなさんの検証魂に火をつけることができたとすればうれしく思います。
さて、SELECT COUNT(1)ですが、これは、1つめの列を指定したCOUNTと一緒なはず。もしかして、1つめって「試験.KEY項目」ですか?

> こあらさん
SELECT * は確かに全列取得ですが、SELECT COUNT(*)が主キーを使わないとかはかなり昔の話だったような。
たとえば、今回検証につかったOracleの場合、
COUNT(*)=rowidであったりINDEXであったりとにかく最適なものでカウント
となっています。
エントリ本文ではINDEX FULL SCANですね。

タイトル  
名前  
Url
コメント