PagingSelectAndQuerySplit

概要

アーキテクト向けの機能です。

基本概念

ページング検索の実データ取得の検索において、select句のデータ取得とレコードの絞り込み処理を二つのSQLに分割します。 (@since 1.0.5G)

分割された一つ目のSQLは、where句はそのままで基点テーブルのPKだけをselect句に並べてPKのリストを検索します。 そして、分割された二つ目のSQLは、そのPKのリストだけを InScope でwhere句に並べ、本来欲しかったselect句のデータを取得します。

e.g. ページング検索のConditionBean @DisplaySql
MemberCB cb = new MemberCB();
cb.setupSelect_MemberServiceAsOne().withServiceRank();
cb.specify().derivedPurchaseList().max(subCB -> {
    subCB.specify().columnPurchasePrice();
}, Member.ALIAS_highestPurchasePrice);
cb.orScopeQuery(orCB -> {
    orCB.query().setMemberStatusCode_Equal_Formalized();
    orCB.query().setMemberName_PrefixSearch("S");
});
cb.query().queryMemberStatus().addOrderBy_DisplayOrder_Asc();
cb.query().addOrderBy_MemberId_Asc();
cb.enablePagingSelectAndQuerySplit();
cb.paging(5, 1);
...
e.g. そして、分割されたSQL @DisplaySql
...
select dfloc.MEMBER_ID as MEMBER_ID
  from MEMBER dfloc
    left outer join MEMBER_SERVICE dfrel_22 on ...
    left outer join SERVICE_RANK dfrel_22_1 on ...
    inner join MEMBER_STATUS dfrel_0 on ...
 where (dfloc.MEMBER_STATUS_CODE = 'FML'
     or dfloc.MEMBER_NAME like 'S%' escape '|'
       ) 
 order by dfrel_0.DISPLAY_ORDER asc, dfloc.MEMBER_ID asc 
 limit 0, 5
...
select dfloc.MEMBER_ID as MEMBER_ID, dfloc.MEMBER_NAME as MEMBER_NAME, ...
     , dfrel_22.MEMBER_SERVICE_ID as MEMBER_SERVICE_ID_22, ...
     , dfrel_22_1.SERVICE_RANK_CODE as SERVICE_RANK_CODE_22_1, ...
     , (select max(sub1loc.PURCHASE_PRICE)
          from PURCHASE sub1loc 
         where sub1loc.MEMBER_ID = dfloc.MEMBER_ID
       ) as HIGHEST_PURCHASE_PRICE
  from MEMBER dfloc
    left outer join MEMBER_SERVICE dfrel_22 on ...
    left outer join SERVICE_RANK dfrel_22_1 on ...
    inner join MEMBER_STATUS dfrel_0 on ...
 where dfloc.MEMBER_ID in (1, 4, 5, 7, 8) 
 order by dfrel_0.DISPLAY_ORDER asc, dfloc.MEMBER_ID asc

あまり、使いたくない機能です。ですが、ごくごく稀に、select句に膨大なサブクエリや関連テーブルのデータ取得が存在しているページング検索をすると、 (DBMSによっては)select句部分の評価が大量に発生してパフォーマンスが劣化することがあります。 (limitでレコードを絞っていても、その絞り込み処理が実行される前に全体の件数の分のselect句評価が実行されることがある)

ディベロッパー向けに思えますが、アーキテクトへの相談無しに利用してはいけません。そういう意味で、"アーキテクト向けの機能" と謳っています。

諸刃の剣

TwoEdgedSword 認定のされた機能です。自分を斬りつけて痛い思いをする可能性のある機能です。 仕組み上、論理的な割り切りもあります。例えば、分割した片方のSQLでは不要な結合もあるかもしれませんが、安全性を考慮して何も調整されません。

最初から、非推奨メソッドです。使うときは明示的に警告を抑制してください。

実装方法

実装の流れ

enablePagingSelectAndQuerySplit() を呼び出して、ページング検索します。seleclPage() だけでなく、fetchFirst() などのlimitによる絞り込みがあれば selectList() でも利用できます。

e.g. SelectAndQuerySplitの実装 @Java
MemberCB cb = new MemberCB();
... // もろもろの条件などを設定
cb.enablePagingSelectAndQuerySplit()
cb.paging(3, 1);

... = memberBhv.selectPage(cb); // 指定されたカラムの条件値は埋め込まれる

メソッド仕様

基本仕様

引数の指定
なし

複合主キーの場合は利用できない

基点テーブルが複合主キーの場合は利用できません。分割されずに普通にページング検索されます。

SpecifiedDerivedOrderByでも利用できる @since 1.0.5J

OrderByで SpecifiedDerivedOrderBy を使っている場合でも利用できます(@since 1.0.5J)。 PKのみのselect文の方でも、SpecifiedDerivedOrderBy で指定されている DerivedReferrer は展開されます。

PagingCountLaterではなくなる

ページング検索の処理の順序が "カウント検索が先" になります。分割すると、DBMS独自のページング機構が効かなくなる可能性があるためです。 (具体的にはMySQLの found_rows() 関数)

呼び出しタイミング

実行時に評価されるので、いつ設定しても構いません。(習慣的に、条件設定後で、実行する直前)

非推奨メソッド

deprecated になっています。将来的に削除されるわけではありませんが、"安易に使ってくれるな" ということを示します。