|
■No97216 (魔界の仮面弁士 さん) に返信
ひろしです。
コメントありがとうございます。
>>実行計画を見てみると、プライマリーキーで検索しているのにもかかわらず >>TABLE ACCESS FULL >>になってしまいます。 > 少なくとも、今回の SQL においては :id_From が NULL の時には全件走査以外の選択肢は無いわけで、 > それが OR で繋がっている以上、事前の実行計画は TABLE ACCESS FULL にならざるを得ないかと。 私はここの認識が不十分でした。 実行計画というのは、パラメータを考慮していない状態での計画と言う事ですね。 私は、OracleCommand のパラメータがどのように設定されているかによって Oracleが解析してくれるものと思っておりました。
ちょっとサンプルが悪かったのですが、よくあるパターンとして 例えば 売上伝票 というテーブルがあって、数年分で数百万件のデータが入っています。
その中から、画面では
入力日 FROM〜TO 伝票日付 FROM〜TO
入力日付を条件とするのか、伝票日付を条件とするのか2択あります。 どちらかの From を入力した場合は、To も必須の条件となります。 実際に抽出される件数は 100万件以上のうちの数百件程度になるでしょうか。
そうした場合
WHERE (入力日 >= :入力日FROM OR :入力日FROM IS NULL) AND (入力日 <= :入力日TO OR :入力日TO IS NULL) AND (伝票日付 >= :伝票日付FROM OR :伝票日付FROM IS NULL) AND (伝票日付 <= :伝票日付TO OR :伝票日付TO IS NULL)
と記述できれば、一番シンプルで見やすいSQLになると考えています。 さらに、エディタでSQLをゴリゴリ直したあと、VBに反映させるのも簡単です。
IF分で書くと
IF 入力日付FROM 条件がある場合 And 入力日 >= :入力日FROM End if If 入力日TO 条件がある場合 And 入力日 <= :入力日TO End if If 伝票日付FROM の条件がある場合 And 伝票日付 >= :伝票日付FROM End if If 伝票日付TO の条件がある場合 And 伝票日付 <= :伝票日付TO End If
となって、エディタで編集したあと、VBに反映させようとしたときに 非常に苦労します。
> 手元に環境が無いので試せませんが、ひとまず OR を外して > WHERE id > CAST(NVL(:id_From, RPAD(CHR(0), 10, CHR(0)) AS CHAR(10))
確かに、以前以下のような条件を試してみたことがあります。
日付FROM の指定がない時は "00000000" ※Date型ではなくCHAR8桁を使用しています。 日付TO の指定がない時は "99999999" に変換して
伝票日付 BETWEEN 伝票日付FROM AND 伝票日付TO
という条件にすると、確かにインデックスが使われ、全てOKと思ったのですが・・・
例えば、伝票日付がNULLで入力日付が設定されている伝票があったとして 入力日付を条件にした場合、 入力日付 BETWEEN '20210401' AND '20210430' And 伝票日付 BETWEEN '00000000' AND '99999999'
となると思うのですが、その場合、伝票日付が Null のデータが除外されてしまう事がわかりました。 (これは拾えないとNGです)
結局はやはり、条件が指定されていない場合 WHERE (入力日 >= :入力日FROM OR :入力日FROM IS NULL) 上記の書き方で、インデックスが使われるようにする方法はないのかと考えておりました。
ただ、実行計画が、OracleParameter の設定値を考慮してくれないのであれば不可能っぽいですね。。。
> PL/SQL の REF CURSOR で受け渡しするようにすれば、PL/SQL の IF で分岐できるかと思います。 本番環境へのリリース等もあるので、外注の身の私としては その手の改良はちょっと厳しいです。
大変ためになりました。 ありがとうございました。
|