InScopeRelation

概要

1.1.x(Java8版)より、ExistsReferrerのオプションに

1.1.x(Java8版)より、こちらの機能は ExistsReferrer のオプションになっています。

existsではなくinScopeSubQueryで

基本概念

in句のサブクエリを使って関連テーブルの条件で絞り込みをする条件(in (select ... from ...))を設定します。InScopeRelation は、絞り込み条件を表す ConditionKey です。

FKの関連を辿り、in句のサブクエリ(相関ではない)を使って、関連テーブルの条件で基点テーブルを絞り込みます。 この条件は、他の方法で実現する "関連テーブルの条件で絞り込み" の代替方法となります。

例えば、親テーブル(ForeignTable: one-to-oneも含む)のカラムの条件で基点テーブルを絞り込むのであれば、Query(Relation) を使って親テーブルの条件を設定し、join を経由して絞り込みを実現するのが通常です。 また、子テーブル(Referrer)のカラムの条件で基点テーブルを絞り込むのであれば、ExistsReferrer を使って子テーブルの条件を設定し、exists を経由して絞り込みを実現するのが通常です。(少なくともDBFluteにおいては)

実際に、InScopeRelation を使った場合と、上記の他の方法を使った場合で、絞り込み条件としての結果は全く同じになります。 InScopeRelation をどういうときに利用するかというと、ほとんどの場合は意識する必要はないと想定されていますが、状況によっては InScopeRelation の方が良いパフォーマンスを出す可能性もあります(もちろん、その逆もあります)。そのときのための代替機能として用意されています。 ただし例外として、InlineView の中では InScopeRelation を利用します(InlineView の中での ExistsReferrerがサポートされていないため)。

会話上では、いんすこーぷりれーしょん と表現します。

ExistsReferrer と InScopeRelation

exists句とin句のサブクエリは、どちらが速いか?よく取り上げられる話題です。 結局、状況とDBMSに依存するので一概にどちらかというのはなかなか結論付けづらいものです。

DBFluteでは、少なくとも "子テーブル対する、exists句の相関サブクエリ、相関でないin句のサブクエリ" において、速度に違い(業務上問題になるほどの違い)が発生するケースは(経験上)多くないと捉え、かつ、業務的な意味合いのフィット感を優先し、 ExistsReferrer をメインとしています。チューニングが必要になったら、InScopeRelation を検討すると。(jfluteは、実務ではほとんど ExistsReferrer を使っています)

SQLの結果が同じなのですから、将来的には、どちらを記述したとしても差が出ないようにDBMS側で完全な最適化がされるようになることを期待したいと思います。 実際、Exampleにて(PostgreSQL-8.4, Oracle 10g XE)、基点テーブル 30000 件、子テーブル 1000000 件で幾つかのパターンを試してみましたが、特にスピードに差は出ませんでした。 (もちろん、もっと色々なパターン、もしくは、データ量を増やせば差が出るのかもしれませんが)

実装方法

実装の流れ

query() の後、inScope[relation-table]() を呼び出し、引数にサブクエリの中の条件を指定する、SubQuery のコールバック実装を指定します。

e.g. InScopeRelationの実装手順 (Eclipseでコード補完) {PURCHASE} @Java
MemberCB cb = new MemberCB();
cb.q // .q と打って enter
--
cb.query()
--
// 1. .ex まで打つと関連テーブル選択
// 2. PL (PurchaseList) で enter
cb.query().inSPL
--

// メソッドが補完されて、引数の "subQuery" が選択状態に
cb.query().inScopePurchaseList(subQuery)
--

// "new " (new + 空白一つ) と打って ctrl + space そして enter
cb.query().inScopePurchaseList(new )
--

// 実装メソッドの空実装が自動生成される (Eclipse-3.5 以上)
cb.query().inScopePurchaseList(new SubQuery<PurchaseCB>() {
    
    public void query(PurchaseCB subCB) {
        // TODO Auto-generated method stub
        
    }
})
--

// ctrl (or command) + D で不要な空行やTODOコメントを消して
// サブクエリ(子テーブル)の絞り込み条件を指定
cb.query().inScopePurchaseList(new SubQuery<PurchaseCB>() {
    public void query(PurchaseCB subCB) {
        subCB.query().setPurchasePrice_GreaterEqual(2000);
    }
}); // セミコロンを忘れずに

SQL上では、コールバックの中で指定された条件がサブクエリの中に展開されます。

e.g. InScopeRelationを使って2000円以上の購入をしたことのある会員を検索 @DisplaySql
...
  from MEMBER dfloc
 where dfloc.MEMBER_ID in (select sub1loc.MEMBER_ID
                             from PURCHASE sub1loc
                            where sub1loc.PURCHASE_PRICE >= 2000
       )

その他、サブクエリの中では、ExistsReferrer と同じような要領で様々なパターンが実現できます。

メソッド仕様

基本仕様

一部を除き、ExistsReferrer と同じです。

OnClause では不可

OnClause の中の条件では利用できません。InlineView の中では利用できます。

サポートされる関連テーブル

全ての関連テーブルに対してサポートされます。

否定条件:NotInScopeRelation

否定条件の対象値の列挙として、NotInScopeRelation もあります。条件が否定になっただけで仕様は InScopeRelation と全く同じです。