集計
SQLには集計関数のSUM
、MIN
、MAX
、COUNT
、AVG
があります。
動的数式または@Aggregationと@Sumを介してプロパティとして、Ebeanでこれらの関数を使用できます。
動的数式
単一属性
LocalDate maxDate =
new QOrder()
.select("max(orderDate)")
.customer.name.equalTo("Rob")
.findSingleAttribute();
select max(t0.order_date)
from orders t0
join customer t1 on t1.id = t0.customer_id
where t1.name = ? ; --bind(Rob)
単一の属性のみを選択する集計クエリでは、返されるタイプはプロパティのタイプと一致します。上のケースでは、orderDateはLocalDate
なので、返されるタイプはそれです。
複数の属性
非集計プロパティ(下の例ではstatus)を指定すると、すべての非集計プロパティを含むGROUP BY
句が生成されます。
List<Order> orders =
new QOrder()
.select("status, max(orderDate)") // status is non-aggregate
.customer.name.equalTo("Rob")
.findList();
select t0.status, max(t0.order_date)
from orders t0
join customer t1 on t1.id = t0.customer_id
where t1.name = ?
group by t0.status -- group by non-aggregate properties
集計クエリによって返されるビーンは部分的に設定されたものです。@Idプロパティが含まれず、したがって遅延読み込みや保存はサポートされません。
上の例のOrderビーンには、statusとorderDateプロパティのみがロードされます。
Fetch
集計クエリには、関連するビーンをロードするためにfetch
またはfetchQuery
を含めることができます。
List<Order> orders
= new QOrder()
.select("status, max(orderDate)")
.customer.fetch("name") // (1) fetch
.status.notEqualTo(Order.Status.NEW)
.findList();
select t0.status, max(t0.order_date), t1.id, t1.name -- (1) has customer id and name
from orders t0
join customer t1 on t1.id = t0.customer_id
where t0.status <> ?
group by t0.status, t1.id, t1.name -- (1) has customer id and name
fetchQuery
を使用する場合、ORMクエリは2つのSQLクエリとして実行されます。
List<Order> orders
= new QOrder()
.select("status, max(orderDate)")
.customer.fetchQuery("name") // (2) fetchQuery ...
.status.notEqualTo(Order.Status.NEW)
.findList();
-- Primary query
select t0.status, max(t0.order_date), t0.customer_id -- (2) has customer id only
from orders t0
where t0.status <> ?
group by t0.status, t0.customer_id
-- Secondary query
select t0.id, t0.name -- (2) customer id and name
from customer t0
where t0.id in (?, ?, ?, ?, ? )
ロードする必要がある多くのプロパティがcustomerにある場合、fetchQuery()を使用するとよい場合があります(これらはすべてプライマリークエリではなくセカンダリSQLクエリに移動するため、GROUP BYの一部にはなりません)。
@Aggregation
@Aggregation
でアノテーションされたプロパティを持つ集計をモデリングできます。これは動的数式を使用する代わりに使用されます。
@Aggregationを持つプロパティはクエリに明示的に含める必要があります(それらは一時的なものとみなされます)。
例
@Entity
@Table(name = "orders")
public class Order extends BaseModel {
...
LocalDate orderDate;
@Aggregation("max(orderDate)") // aggregation property
LocalDate maxOrderDate;
@Aggregation("count(*)") // aggregation property
Long totalCount;
maxOrderDateとtotalCountプロパティを追加すると、クエリのselect
句とhaving
句で使用できます。
QOrder o = QOrder.alias();
List<Order> orders = new QOrder()
.select(o.status, o.maxOrderDate, o.totalCount)
.findList();
select t0.status, max(t0.order_date), count(*)
from orders t0
group by t0.status
Having
集計プロパティの述部はHaving
句に追加する必要があります。
List<Order> orders = new QOrder()
.select(o.status, o.maxOrderDate, o.totalCount)
.status.notEqualTo(Order.Status.COMPLETE) // (1) where clause - non aggregate properties
.having()
.totalCount.greaterThan(1) // (2) having clause - aggregate properties
.findList();
select t0.status, max(t0.order_date), count(*)
from orders t0
where t0.status <> ? // (1)
group by t0.status
having count(*) > ? // (2)
集計ビーン
1つか2つの集計プロパティのみが必要な場合は、それらを既存のエンティティビーンに追加しても問題ありません。ただし、代わりに多くのプロパティを集計する必要がある場合は、このアプローチはモデルをいじっており、見苦しくなり始めます。
集計ビーンを作成してプロパティをモデル化できます - sum / group by / 集計ビューをモデル化します。
- @Tableではなく@Viewを使用します。
- 保存したり削除したりしないので、Modelを拡張しません。
- @Idまたは@Versionプロパティがありません
- 集計(合計、最大値など)したいプロパティがあります
- グループ化(日付、ステータス、ManyToOne)したいプロパティがあります
例
MachineUseと呼ばれるエンティティBeanがあるとします
@Entity
@Table(name = "machine_use")
public class MachineUse extends Model {
@Id
private long id;
@ManyToOne(optional = false)
private DMachine machine;
private LocalDate date;
private long distanceKms; // we want to sum() this ...
private long timeSecs; // we want to sum() this ...
private BigDecimal fuel; // we want to sum() this ...
@Version
private long version;
...
次のように集計Beanを作成できます
@Entity
@View(name = "machine_use")
public class MachineUseAggregate {
@ManyToOne(optional = false)
private DMachine machine; // group by, fetch, fetchQuery on ...
private LocalDate date; // group by on ...
@Aggregation("sum(distanceKms)")
private Long distanceKms;
@Aggregation("sum(timeSecs)")
private Long timeSecs;
@Aggregation("sum(fuel)")
private BigDecimal fuel;
@Aggregation("count(*)")
private Long count;
...
@Sum
集計Beanを作成すると、次のようにプロパティを合計するパターンがよく見られます
@Aggregation("sum(<property name>)")
Type <property name>;
たとえば、次のようなBeanがあります
@Aggregation("sum(distanceKms)")
BigDecimal distanceKms;
@Aggregation("sum(useSeconds)")
Long useSeconds;
@Aggregation("sum(cost)")
BigDecimal cost;
@Sum
はこのパターンの構文糖で、上記のようになります
@Sum
BigDecimal distanceKms;
@Sum
Long useSeconds;
@Sum
BigDecimal cost;