2009-07-07 11 views
5

this questionのディスカッションの一部を読み、自分のPL/SQLコードで、 ROWNUM = 1最適化を使用しません。私が持っているROWNUM = 1の場合、「存在」スタイルクエリのパフォーマンスが大幅に向上します

質問は以下のとおりです。ROWNUM = 1の導入は、大幅にパフォーマンスを

  1. 増加していますか?私はそれを決定しようとしているそうであれば、条件はパフォーマンスが特に改善されるものの下
  2. (加入の例えば多く、インデックスのないカラム、大きなテーブル、上の制約大きな結果セット)

は価値がすべて書き換えです既存のクエリのROWNUM = 1最適化を追加します。

私が考えているクエリは、複数の結合があり、大きなテーブルを照会するものです。私は、これはあなたの時間の価値であることを行っていないことを推測するつもりだ

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions> 
AND ROWNUM = 1; 

IF <local variable> > 0 THEN 
    <do stuff> 
END IF; 
+0

最初のクエリは、SELECT 1ではなくSELECT COUNT(1)を持つことを意図していますか? –

+0

SELECT 1(つまり、行数をカウントするのではなく、返される行に定数を選択します) – darreljnz

答えて

7

単純な単一の索引検索で解決できない問合せでは、パフォーマンスが大幅に向上します(平均数十万)。テーブル結合。 しかし、データ/アプリケーションエラーを隠す可能性があります。可能な限りの例では、同じくらい簡単にするためにPKを使用していない--intentionally

create table t (id number(10,0), padding varchar2(1000)); 

はテーブルを持っています。今、あなたはDBのテーブル全体かどうかを経由しなければならない

select 1 into ll_exists 
from t where id = 5; 

ような何かを頼む場合

insert into t (id, padding) 
select rownum, rpad(' ', 1000) from dual connect by level < 10000 

:パディングは、各レコード多数のレコードを持つ

に実際のデータロードをシミュレートするために使用されます最初のデータブロック(これはさまざまな方法で挿入することができないため、わからない)または唯一の一致するレコードが見つかりました。これは、一致するレコードが1つしかないことを知らないためです。一方、...とrownum = 1を使用すると、別の一致するレコードがない(または必要ではない)ということを伝えたため、レコードが見つかった後にデータを走査するのを止めることができます。

欠点は、データに複数の可能なレコードが含まれている場合、rownum制約で不確定的な結果が得られることです。 クエリが

select id into ll_id 
from t where mod (id, 2) = 1 
and rownum = 1; 

だった場合、私はDBの答え1と同様に3と同様に123から受け取ることができる...順序は保証され、これは結果であるされていません。 (rownum節なしでは、私はTOO_MANY_ROWS例外が発生します。どちらが悪いのかによって決まります)

本当に存在をテストするクエリが必要な場合は、それをそのまま書いてください。

begin 

select 'It does' 
    into ls_exists 
from dual where 
exists (your_original_query_without_rownum); 

do_something_when_it_does_exist 
exception 
    when no_data_found then 
    do_something_when_it_doesn't_exist 
end; 
+0

+1、ローバン述語の非決定論的性質に関する良好な詳細な応答+注記。 –

+0

私は最後に結論が好きです - それはそれを書くより適切な方法です。 – darreljnz

+0

外部クエリは、デュアルから選択カウント(*)することができます...(それは1または0を萎縮させるでしょう)ので、プログラム・ロジックを実行するために例外ブロックを使用する必要はありません(これは悪い)。 –

2

:私はそれらを変更すること検討している

SELECT 1 
INTO ln_count 
FROM table_1, table_2...., table_n 
WHERE <various joins and conditions>; 

IF ln_count > 0 THEN 
    <do stuff> 
END IF; 

:彼らはの一般的な形式を持っています。近代的なオプティマイザは非常に優れているので、ROWUM = 1を追加することでパフォーマンスを大幅に向上させることができます。

この制約を確認する必要性がなくなると、パフォーマンスが向上しますか?

私はしばしば自分自身に深く墓を掘るオプティマイザを信頼停止したとき、私は見つける;)さらに

: 疑いで、それを試してみてください。大きな結合を見つけて、rownum = 1を使わずに何回か実行し、rownum = 1で何度か実行し、大きな改善が見られるかどうか確認してください。キャッシュの問題がないようにするには、データベースを再起動することをお勧めします。

+1

私たちはすべて、賢明な新しいアイデアはありません。 – dkretz

+0

クエリは1つの行を返すように「許可」されていますが、結果があるかどうかだけを気にするので、最初の行以降は何も無視されます。 私のコードの実用的な例です。私は顧客注文を含む大きなテーブルを持っています。各注文にはステータス(「保留中」、「開封済み」、「閉鎖済み」、「キャンセル済み」など)と商品IDがある場合があります。私は、特定の製品識別子のオープンオーダーがあるかどうかを調べるための照会があります。私は、理論的には、パフォーマンスの向上があることを知っています。私が興味を持っているのは、実際には大きなパフォーマンスが得られるかどうかです。 – darreljnz

+0

「1つの行を返すことを許可する」クエリ(例えば、集計を計算するクエリ)と、1つの行のみを調べるクエリ(ROWNUM = 1を持つクエリなど)との間には違いがあります。比較:SELECT COUNT(*)FROM my_big_table; SELECT COUNT(*)from my_big_tableどこROWNUM = 1;結果は(パフォーマンス面で)非常に異なるでしょう。 –

5

ホットスポットを修正する必要がある場合を除き、最適化の経験則があります。ただし、パフォーマンス上の利点が不明な場合は、両方を使用していくつかのテストを実行して、パフォーマンスの向上を測定できるかどうかを確認することをお勧めします。

wikipedia quotesドナルド・クヌース言うように:

は、「我々は小さな効率を忘れなければならない、時間の約97%を言う:時期尚早の最適化は諸悪の根源です。」

+0

+1、良いアドバイスと素敵なお言葉 – DCookie

0

COUNT(1)を求める場合、Oracleは正確な答えを満たすためにすべての一致する行を見つける必要があります。

SELECT COUNT(1) FROM .... 

最初の行から1を要求すると、一致する行が1つ見つかったら停止できます。

SELECT 1 FROM ... WHERE ROWNUM = 1 

実際に必要なデータのみを尋ねることをお勧めします。最初のもののみを気にしていると、オラクルは1,203,499件のマッチング結果があると教えてくれるのはなぜですか?人々はオプティマイザが物事を改善できると述べています。しかし、それはあなたが依頼した質問にまだ答えなければなりません。簡単な質問をすると、答えが早く出てくることがあります。パフォーマンスに大きな影響を与える可能性がある

回: - あなたが見つけたレコードの *実際のカウントはHASHから * Oracleのスイッチは、ネストされたループに計画に参加、高く、ネストされたループの計画があります最初の行を見つけるのが良い

+0

COUNT(1)は1行しか取得できないという意味ではありません。 SELECT COUNT(*)FROM my_big_table; SELECT COUNT(1)FROM my_big_table;とまったく同じです。 –

+0

私はCOUNT(*)対COUNT(1)についてのポイントを作っていませんでした。あなたがCOUNT(1)を求めたら、それらをすべて数える必要があります。あなたが最初に一致する行に1を求めるなら、それは1つだけを見つける必要があります。 –

+0

十分に公正です。個人的には、初心者のための混乱を避けるために「COUNT(1)」の使用は避けてください。 –

0

ROWNUM = 1の導入はパフォーマンスを大幅に向上させますか?

非常に大きな違いがあります。最初の行だけに興味がある場合は、クエリが実行されたときにデータベースが検索されます。次に、「ROWNUM = 1」を追加することによってOracleに伝えることをお勧めします。そうしないと、Oracleは問合せからすべての行を最終的にフェッチすることを想定しており、それに従って問合せを最適化します。

COUNT()の場合、少なくとも1つのレコードがあるかどうかだけを知りたい場合、クエリオプティマイザはそのことを知らず、各行を数えます。これは時間の無駄です。 ROWNUM = 1を追加すると、オプティマイザに行が見つかるとすぐに停止することができます。

もしそうなら、下の条件が性能が特に (加入の例えば多く、インデックスのないカラム、大きなテーブル上の制約、大きな 結果セット)

クエリがする必要があり、より多くのデータを改善するだろうかROWUM = 1述語がパフォーマンスを向上させる可能性が高い場合は、クエリに答えるためにPlow Throughを実行します。たとえば、複数表結合では、ROWNUM = 1を追加すると、高価なハッシュ・ジョインを多く使用することからプランが変更され、ネストされたループが使用されます。

+0

結論:ROWNUM = 1を追加することは単なる「最適化」の尺度ではなく、各クエリが行うべきことを正確に行うための重要な部分と考えられます。 –

+0

うわー、私の最初のdownvote! :) –

+0

ここに何が間違っているかについて誰でも気にかけていますか?ちょうど私の啓発のために... –

1

私は最高の答えを投票したいとしながら、例外を回避するための努力で、私はこのような何か:

begin 

    select count(*) 
    into ls_exists 
    from dual 
    where exists (select null from ... where ...); 

    if ls_exists = 1 then 
    do_something; 
    else 
    do_something_else; 
    end if; 

end; 

をこれが常に1または0で単一の行を返すます。 例外なく。

SELECT NULLの使用にも注意してください。これはいくつかのことを行います。オプティマイザが適切であると判断した場合、オラクルは索引のみを参照します。 SELECT句に表の列を配置し、その列が索引の一部でない場合、Oracleは索引の参照を行い、表から行を取得します。これは問合せによってはまったく役に立たない可能性があります。行の存在をチェックするだけなので、おそらく実際のデータは必要ありません。

SELECT NULLはデータを返しません(最適化)。EXISTS句は、行が返されるかどうかを調べます。

関連する問題