ManualOrder

概要

基本概念

特殊条件を付けて order by を指定します。例えば...

優先処理
指定された値を優先して並べる e.g. SEA, LAND を先に並べて、後は別の条件
計算処理
カラムに足し算、掛け算など計算をして並べる e.g. PURCHASE_COUNT * 2
関数処理
カラムに関数を仕掛けて並べる e.g. trunc(PURCHASE_DATETIME)

会話上では、まにゅあるおーだー と表現します。

優先処理

指定された値を優先して並べます。

また、正式会員と仮会員だけは最初に並べて、それ以外の会員は単に更新日時で並べるというような、ある値だけを優先するソート要件がある場合などにも対応できます。 "24時間以内に更新されたものを先に並べる" というような条件で指定することもできます。

アプリでは、並べる順番の値のリストを指定することで、order by の中で case when 構文を使ってそのソート条件を実現します。 必ず OrderBy と組み合わせて指定します。

計算処理

カラムに足し算、掛け算など計算をして並べます。主に、数値カラムで利用します。

計算処理が入る分、処理コストはかかりますし、インデックスが活用できなくなる可能性もあるので、パフォーマンスのことを考えると 気軽に使う機能ではない ということは念頭に置きましょう。

関数処理

カラムに関数を仕掛けて並べます。主に、数値カラムで利用します。

関数処理が入る分、処理コストはかかりますし、インデックスが活用できなくなる可能性もあるので、パフォーマンスのことを考えると 気軽に使う機能ではない ということは念頭に置きましょう。(DBMSによっては、関数インデックスを作れるものもありますので、そういったものも検討すると良いでしょう)

実装方法

実装の流れ ※1.1.x (Java8版)

まずは、OrderBy のメソッド呼び出しの直後に続けて、withManualOrder() を呼び出し、コールバックで特殊条件を指定します。Lambda の引数は、ManualOrderBean です。

e.g. 指定された値の順番通りに並べる実装手順 {MEMBER_STATUS_CODE} @Java
// 会員を正式会員、仮会員を先に並べるとする
// (区分値カラムであれば、CDef 自体を値としてそのまま利用できる)
List<CDef.MemberStatus> orderValueList = new ArrayList<CDef.MemberStatus>();
orderValueList.add(CDef.MemberStatus.Formalized);  // 正式会員
orderValueList.add(CDef.MemberStatus.Provisional); // 仮会員

// cb: MemberCB
// OrderBy指定の後、.wi まで打ってメソッドを選択
cb.query().addOrderBy_MemberStatusCode_Asc().wi
--

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

// _ll で補完 (DBFlute補完テンプレートが有効なら)
cb.query().addOrderBy_MemberStatusCode_Asc().withManualOrder(_ll);
--

// Lambda引数名は op にして...色々な特殊条件を指定
cb.query().addOrderBy_MemberStatusCode_Asc().withManualOrder(op -> {
    op.acceptOrderValueList(orderValueList); // 優先処理
});
cb.query().addOrderBy_MemberName_Asc(); // その後のソート順
e.g. 正式会員、仮会員を先に並べる @DisplaySql
select ...
  from MEMBER dfloc  
 order by 
   case
     when dfloc.MEMBER_STATUS_CODE = 'FML' then 0
     when dfloc.MEMBER_STATUS_CODE = 'PRV' then 1
     else 2
   end asc, dfloc.MEMBER_NAME asc

オプションのメソッド

優先処理
op.accessOrderValueList(), op.when_LessThan(), op.when_FromTo(), ...
計算処理
op.plus(), op.minus(), op.multiply(), ...
関数処理
op.convert(convOp -> convOp.truncTime())

条件判定でので優先処理

例えば、24時間以内に更新されたデータを優先して並べるが、それより前のものに関しては別のカラムの値で並べる。 この場合、日付の大なり小なり比較が必要になりますが、ManualOrderBean で比較演算子を指定して条件を追加することができます。

e.g. 24時間以内に更新されたデータを優先して並べる {UPDATE_DATETIME} @Java
cb.query().addOrderBy_UpdateDatetime_Asc().withManualOrder(op -> {
    op.when_GreaterThan(date24before); // 更新日時 > 24時間前
});
cb.query().addOrderBy_MemberName_Asc(); // その後のソート順

一つのケース(when)に対して複数の条件を連結させることもできます。whenメソッドで一つ目の条件を追加した後に続けて and_ もしくは、or_ で二つ目以降の条件を追加します。ただし、三つ以上の条件を連結する際、一つのケース内で利用できる連結子(and/or)は一つです。and と or を組み合わせて利用することはできません。

e.g. 会員IDが 5 から 10 の会員を優先して並べる {MEMBER_ID} @Java
cb.query().addOrderBy_MemberId_Asc().withManualOrder(op -> {
    op.when_GreaterEqual(5).and_LessEqual(10); // 5 から 10
});
cb.query().addOrderBy_MemberName_Asc(); // その後のソート順

SpecifiedDerivedOrderBy との組み合わせ

SpecifiedDerivedOrderBy に対しても利用できます。

メソッド仕様

基本仕様

適用対象
ManualOrder は、直前の OrderBy に対してのみ適用されます。
nullの指定
nullが指定されたら例外です。また、リストの中の null 要素は無視されます。
空リストの指定
acceptOrderValueList()に空リストが指定されたらその ManualOrder は無効です。もし、同じカラムに対して既に一度 ManualOrder を設定している場合は、その設定を上書きして、ManualOrder を無効化します。
ケースの条件値
ManualOrderBean の when メソッドの引数の条件値は必須です。null は例外です。

読み取り専用リストでも可

内部では指定されたリストに更新操作はしないので、読み取り専用リストも指定できます。よって、Arrays.asList() を使って、シンプルにリストを構築することもできます。

CDef型の利用

区分値カラムに関しては、CDef型(厳密には Classification 型)の値をそのまま利用できます。(SQL上ではコード値が利用されます)

UnionQuery と case when

DBMSに関わらず、UnionQuery を使った時の ManualOrder はサポートされません。

必ず OrderBy の後に

OrderBy のメソッド呼び出し直後以外のタイミングでは呼び出してはいけません。

同カラムに対する複数回呼び出し

一つのカラムに対して、ManualOrder を複数回呼び出しても上書きされるだけです。

サポートされる型

SQLの文法的に許される限り全ての型に対してサポートされます。