NEW: FetchCountDistinct - query can return distinct values with their counts (#1300)

* NEW: FetchCountDistinct - query can return distinct values with their counts

* Have to disable asserts, because of #1298
This commit is contained in:
Roland Praml
2018-03-02 11:46:41 +13:00
committed by Rob Bygrave
parent 021d57f4b2
commit c1b022c123
11 changed files with 329 additions and 83 deletions
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.CountDistinctOrder;
import io.ebean.Query;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
@@ -190,7 +191,7 @@ class CQueryBuilder {
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
predicates.prepare(false);
return new CQueryFetchSingleAttribute(request, predicates, queryPlan);
return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct());
}
// use RawSql or generated Sql
@@ -201,7 +202,7 @@ class CQueryBuilder {
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchSingleAttribute(request, predicates, queryPlan);
return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct());
}
/**
@@ -549,8 +550,13 @@ class CQueryBuilder {
}
}
}
sb.append(select.getSelectSql());
if (query.isCountDistinct() && query.isSingleAttribute()) {
sb.append("r1.attribute_, count(*) from (select ");
sb.append(select.getSelectSql());
sb.append(" as attribute_");
} else {
sb.append(select.getSelectSql());
}
if (query.isDistinctQuery() && dbOrderBy != null && !query.isSingleAttribute()) {
// add the orderBy columns to the select clause (due to distinct)
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
@@ -642,10 +648,15 @@ class CQueryBuilder {
sb.append(" having ").append(dbHaving);
}
if (dbOrderBy != null) {
if (dbOrderBy != null && !query.isCountDistinct()) {
sb.append(" order by ").append(dbOrderBy);
}
if (query.isCountDistinct() && query.isSingleAttribute()) {
sb.append(") r1 group by r1.attribute_");
sb.append(toSql(query.getCountDistinctOrder()));
}
if (useSqlLimiter) {
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform);
@@ -657,6 +668,25 @@ class CQueryBuilder {
}
private String toSql(CountDistinctOrder orderBy) {
switch(orderBy) {
case ATTR_ASC:
return " order by r1.attribute_";
case ATTR_DESC:
return " order by r1.attribute_ desc";
case COUNT_ASC_ATTR_ASC:
return " order by count(*), r1.attribute_";
case COUNT_ASC_ATTR_DESC:
return " order by count(*), r1.attribute_ desc";
case COUNT_DESC_ATTR_ASC:
return " order by count(*) desc, r1.attribute_";
case COUNT_DESC_ATTR_DESC:
return " order by count(*) desc, r1.attribute_ desc";
default:
throw new IllegalArgumentException("Illegal enum: "+ orderBy);
}
}
/**
* Append where or and based on the hasWhere flag.
*/
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.query;
import io.ebean.util.JdbcClose;
import io.ebean.CountedValue;
import io.ebeaninternal.api.SpiProfileTransactionEvent;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiTransaction;
@@ -60,18 +61,21 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
private final ScalarType<?> scalarType;
private final boolean containsCounts;
private long profileOffset;
/**
* Create the Sql select based on the request.
*/
CQueryFetchSingleAttribute(OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan queryPlan) {
CQueryFetchSingleAttribute(OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan queryPlan, boolean containsCounts) {
this.request = request;
this.queryPlan = queryPlan;
this.query = request.getQuery();
this.sql = queryPlan.getSql();
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
this.containsCounts = containsCounts;
this.scalarType = queryPlan.getSingleAttributeScalarType();
query.setGeneratedSql(sql);
}
@@ -101,7 +105,11 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
List<Object> result = new ArrayList<>();
while (dataReader.next()) {
result.add(scalarType.read(dataReader));
Object value = scalarType.read(dataReader);
if (containsCounts) {
value = new CountedValue<>(value, dataReader.getLong());
}
result.add(value);
dataReader.resetColumnPosition();
rowCount++;
}
@@ -110,7 +110,7 @@ public final class SqlTreeBuilder {
this.query = request.getQuery();
this.temporalMode = SpiQuery.TemporalMode.of(query);
this.disableLazyLoad = query.isDisableLazyLoading();
this.subQuery = Type.SUBQUERY == query.getType() || Type.ID_LIST == query.getType();
this.subQuery = Type.SUBQUERY == query.getType() || Type.ID_LIST == query.getType() || Type.DELETE == query.getType() || query.isCountDistinct();
this.includeJoin = query.getM2mIncludeJoin();
this.manyWhereJoins = query.getManyWhereJoins();
this.queryDetail = query.getDetail();