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