NullsFirst/Last

概要

基本概念

null のデータを先に並べるか後に並べるかを設定します。

SQLにおいて order by で指定されたカラムに null があり得る場合に、null のデータが先に並ぶか後に並ぶか、DBMSに依存します。その仕様に依存した実装をしていると、DBMSの移行をする際のトラブルになります。 また、そもそも使っているDBMSの仕様がどっちなのかをいちいち調べて、とするのも無駄な作業です。 アプリ側で、null を先(or 後)に並べるようにと明示的な指定ができるのが一番です。

DBFluteでは、nulls first 構文を使って解決します。但し、この構文を使っていないDBMS(例えば、MySQL, DB2, SQLServerなど)では、case when 構文をつかって実現します。

必ず OrderBy と組み合わせて指定します。Nullableカラムでソートする場合は必ず指定しましょう。

会話上では、ぬるずふぁーすと/らすと と表現します。

実装方法

実装の流れ

まずは、OrderBy のメソッド呼び出しの直後に続けて、withNulls[First or Last]() を呼び出します。

e.g. 昇順のOrderByでnullを先に並べる実装手順 (Eclipseでコード補完) {BIRTHDATE} @Java
MemberCB cb = new MemberCB();
cb.query().addOrderBy_Birthdate_Asc().wi // .wi まで打ってメソッドを選択
--
cb.query().addOrderBy_Birthdate_Asc().withNullsFirst()
...
e.g. 生年月日の昇順で null を先に並べる {PostgreSQL} @DisplaySql
...
 order by dfloc.BIRTHDATE asc nulls first
...
e.g. 生年月日の昇順で null を先に並べる {MySQL} @DisplaySql
...
 order by case when dfloc.BIRTHDATE is not null then 1 else 0 end asc, dfloc.BIRTHDATE asc
...

SpecifiedDerivedOrderBy との組み合わせ

SpecifiedDerivedOrderBy に対しても利用することができます。導出カラムは関数の仕様として、データ存在しない場合に null になるケースが想定されますので、DerivedReferrerOption で coalesce を利用していない場合は、NullsFirst/Last が必要になる場面が多いと想定されます。

e.g. 最終ログイン日時の昇順で、ログインしたことない人を後に並べる @Java
...
cb.query().addSpecifiedDerivedOrderBy_Asc(Member.ALIAS_latest...).withNullsLast();
...

メソッド仕様

基本仕様

NullsFirst/Last は、直前の OrderBy に対してのみ適用されます。

UnionQuery と case when

case when 構文を使って実現するDBMSで、かつ、union を利用した時の order by 句に case when 構文が許されないDBMS(例えば、DB2, SQLServerなど)においては、UnionQuery を使った時の NullsFirst/Last はサポートされません。

必ず OrderBy の後に

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

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

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

サポートされる型

全ての型に対してサポートされます。

NotNull のカラムでも利用できますが、業務的な意味はありません。

Nullableカラムには必ず

一番、やってはいけないのが、ソートキーが null があり得る(Nullable)カラムであることを見逃して、実装を終えてしまうことです。 業務で null が先に並んで欲しい場面は少ないように考えられますが、たまたまそういうDBMSを使っていたとして、 リリース後に実務のデータが入ったときに初めてユーザからの通知で判明する、というような状況が発生するとややこしい話になります。

そもそも業務仕様として "データなし(null)" のレコードをどっちに並べるかは明確にする必要があります。 もし仕様で明確かされていないのであれば、NullsFirst/Last を使って実装する前に、仕様を正す方が先でしょう。