2

2 つのクエリがあります。

with tmp as (
select asy.aim_student_id, ast.aim_test, asq.response
  from aim_student_test ast
  join aim_student_qst asq on (asq.aps_yr = ast.aps_yr and asq.aim_test = ast.aim_test and asq.aim_id = ast.aim_id)
  join aim_student_yr asy on (asy.aps_yr = ast.aps_yr and asy.aim_student_yr_id = ast.aim_student_yr_id)
    where asq.aps_yr = '2012'
    and asq.qst_num = 1)
select aim_student_id, aim_test, response  
  from tmp
  where response is null
  -- execution-time: 0.032 seconds


define this_year = extract(year from sysdate)
with tmp as (
select asy.aim_student_id, ast.aim_test, asq.response
  from aim_student_test ast
  join aim_student_qst asq on (asq.aps_yr = ast.aps_yr and asq.aim_test = ast.aim_test and asq.aim_id = ast.aim_id)
  join aim_student_yr asy on (asy.aps_yr = ast.aps_yr and asy.aim_student_yr_id = ast.aim_student_yr_id)
    where asq.aps_yr = &this_year
    and asq.qst_num = 1)
select aim_student_id, aim_test, response  
  from tmp
  where response is null
  -- execution-time: 82.202 seconds

唯一の違いは、1 つは「2012」を使用し、もう 1 つは抽出 (sysdate からの年) を実装していることです。

オラクルがチェックするすべてのレコードに対して抽出(sysdateからの年)を計算していることしか想像できません。これを一度計算して変数として使用する方法がわかりません。検索しても、探している答えが返ってこないので、SO.com のマジシャンにたどり着きました。どうすれば適切に使用できますか

extract(year from sysdate)

変数として?

4

2 に答える 2

3

クエリでを使用すると、 string&this_yearが置換されるため、2 番目のクエリは実際には次のようになります。 extract(year from sysdate)

where asq.aps_yr = extract(year from sysdate)

これは、2 番目の説明プランからわかります。それ自体はおそらく問題ではありません。それを遅くしている可能性があるのは、これを行うことが計画を anindex range scanから an index skip scanagainstに変更していることaim_student_qstp1です。asq.aps_yr本当の違いは、高速バージョンでは文字列 ( )と比較し'2012'、2 番目では数値 ( 2012) と比較することです。また、説明計画にも示されているように、これによりto_number(asq.aps_yr)、インデックスが停止していることが原因です。期待どおりに使用されます。

コードでこれを修正するには、次のようにします。

where asq.aps_yr = to_char(&this_year)

クエリを実行する前に一度計算してから変数として使用する場合、少なくとも 2 つの方法があります (SQL*Plus/SQL Developer の場合)。置換変数を使用すると、次columnの代わりにコマンドを使用できますdefine

column tmp_this_year new_value this_year
set termout off
select extract(year from sysdate) as tmp_this_year from dual;
set termout on
set verify off

...これにより&this_year=2012termout変更により、実際の取得verifyが非表示になり、置換が使用されたときに通知が停止します。両方とも、スクリプトで余分な出力が得られません)、クエリを次のように変更します。

where asq.aps_yr = '&this_year'

...そのため、値は文字列として扱われ、to_char()不要になります。

または、バインド変数を使用できます。

var this_year varchar2(4);
set feedback off;
exec :this_year := extract(year from sysdate);

...そして、クエリは次のとおりです。

where asq.aps_yr = :this_year

この場合、バインド変数はすでに文字列として定義されているため、引用符は必要ないことに注意してくださいexec。それを設定する には暗黙的な変換があります。

于 2012-07-20T07:18:35.337 に答える
2

違いは、日付から年を抽出したためだとは思いません。2番目のケースでは変数を使用しているため、Oracleは年を1回しか抽出しないと確信しています。

違いは、クエリで使用される実行パスによるものです。違いを実際に確認するには、実行計画を投稿する必要があります。明示的な定数を使用すると、最適なクエリ プランを選択するためのより多くの情報がオプティマイザに提供されます。

たとえば、データが年ごとにパーティション化されている場合、一定の年を使用すると、Oracle はどのパーティションにデータがあるかを判断できます。2 番目のケースでは、Oracle が値を定数として認識しない可能性があり、すべてのデータ パーティションを読み取る必要があります。これは起こりうることのほんの一例です。この場合、Oracle が何をするかはわかりません。

于 2012-07-20T01:02:34.793 に答える