mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1404 - Query.findCount() with query.setDistinct(true) ... doesn't give count distinct SQL query
This commit is contained in:
@@ -1294,8 +1294,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> int findCount(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
return findCountWithCopy(copy, t);
|
||||
SpiQuery<T> spiQuery = ((SpiQuery<T>) query);
|
||||
if (!spiQuery.isDistinct()) {
|
||||
spiQuery = spiQuery.copy();
|
||||
}
|
||||
return findCountWithCopy(spiQuery, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -81,6 +81,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private BeanPropertyAssocMany<?> manyProperty;
|
||||
|
||||
private boolean inlineCountDistinct;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -742,4 +744,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
public void slowQueryCheck(long executionTimeMicros, int rowCount) {
|
||||
ebeanServer.slowQueryCheck(executionTimeMicros, rowCount, query);
|
||||
}
|
||||
|
||||
public void setInlineCountDistinct() {
|
||||
inlineCountDistinct = true;
|
||||
}
|
||||
|
||||
public boolean isInlineCountDistinct() {
|
||||
return inlineCountDistinct;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +241,14 @@ class CQueryBuilder {
|
||||
|
||||
ManyWhereJoins manyWhereJoins = query.getManyWhereJoins();
|
||||
|
||||
if (manyWhereJoins.isFormulaWithJoin()) {
|
||||
query.select(manyWhereJoins.getFormulaProperties());
|
||||
} else {
|
||||
query.setSelectId();
|
||||
boolean countDistinct = query.isDistinct();
|
||||
if (!countDistinct) {
|
||||
// minimise select clause for standard count
|
||||
if (manyWhereJoins.isFormulaWithJoin()) {
|
||||
query.select(manyWhereJoins.getFormulaProperties());
|
||||
} else {
|
||||
query.setSelectId();
|
||||
}
|
||||
}
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
@@ -263,23 +267,35 @@ class CQueryBuilder {
|
||||
}
|
||||
|
||||
boolean hasMany = sqlTree.hasMany();
|
||||
String sqlSelect = "select count(*)";
|
||||
if (hasMany) {
|
||||
// need to count distinct id's ...
|
||||
query.setSqlDistinct(true);
|
||||
sqlSelect = null;
|
||||
|
||||
String sqlSelect = null;
|
||||
if (countDistinct) {
|
||||
if (sqlTree.isSingleProperty()) {
|
||||
request.setInlineCountDistinct();
|
||||
}
|
||||
} else {
|
||||
if (hasMany) {
|
||||
// need to count distinct id's ...
|
||||
query.setSqlDistinct(true);
|
||||
} else {
|
||||
sqlSelect = "select count(*)";
|
||||
}
|
||||
}
|
||||
|
||||
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
|
||||
String sql = s.getSql();
|
||||
if (hasMany || query.isRawSql()) {
|
||||
int pos = sql.lastIndexOf(" order by "); // remove order by - mssql does not accept order by in subqueries
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
sql = "select count(*) from ( " + sql + ")";
|
||||
if (selectCountWithAlias) {
|
||||
sql += " as c";
|
||||
|
||||
if (!request.isInlineCountDistinct()) {
|
||||
if (countDistinct) {
|
||||
sql = wrapSelectCount(sql);
|
||||
|
||||
} else if (hasMany || query.isRawSql()) {
|
||||
// remove order by - mssql does not accept order by in subqueries
|
||||
int pos = sql.lastIndexOf(" order by ");
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
sql = wrapSelectCount(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +306,14 @@ class CQueryBuilder {
|
||||
return new CQueryRowCount(queryPlan, request, predicates);
|
||||
}
|
||||
|
||||
private String wrapSelectCount(String sql) {
|
||||
sql = "select count(*) from ( " + sql + ")";
|
||||
if (selectCountWithAlias) {
|
||||
sql += " as c";
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL Select statement as a String. Converts logical property
|
||||
* names to physical deployment column names.
|
||||
@@ -536,6 +560,9 @@ class CQueryBuilder {
|
||||
if (!useSqlLimiter) {
|
||||
sb.append("select ");
|
||||
if (query.isDistinctQuery()) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
}
|
||||
sb.append("distinct ");
|
||||
String distinctOn = select.getDistinctOn();
|
||||
if (distinctOn != null) {
|
||||
@@ -551,6 +578,9 @@ class CQueryBuilder {
|
||||
} else {
|
||||
sb.append(select.getSelectSql());
|
||||
}
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (query.isDistinctQuery() && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
|
||||
@@ -159,4 +159,8 @@ class SqlTree {
|
||||
boolean hasMany() {
|
||||
return manyProperty != null || rootNode.hasMany();
|
||||
}
|
||||
|
||||
boolean isSingleProperty() {
|
||||
return rootNode.isSingleProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,4 +83,8 @@ interface SqlTreeNode {
|
||||
*/
|
||||
ScalarType<?> getSingleAttributeScalarType();
|
||||
|
||||
/**
|
||||
* Return true if the query is known to only have a single property selected.
|
||||
*/
|
||||
boolean isSingleProperty();
|
||||
}
|
||||
|
||||
@@ -138,6 +138,11 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return properties != null && properties.length == 1 && children.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
if (properties == null || properties.length == 0) {
|
||||
|
||||
@@ -39,6 +39,11 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildRawSqlSelectChain(List<String> selectChain) {
|
||||
// nothing to add
|
||||
|
||||
@@ -36,6 +36,11 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
this.parentPrefix = split[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
throw new IllegalStateException("No expected");
|
||||
|
||||
@@ -3,13 +3,16 @@ package org.tests.query;
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestRowCount extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -17,8 +20,12 @@ public class TestRowCount extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class).fetch("details").where().gt("id", 1)
|
||||
.gt("details.id", 1).order("id desc");
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.fetch("details")
|
||||
.where()
|
||||
.gt("id", 1)
|
||||
.gt("details.id", 1)
|
||||
.order("id desc");
|
||||
|
||||
int rc = query.findCount();
|
||||
|
||||
@@ -30,8 +37,40 @@ public class TestRowCount extends BaseTestCase {
|
||||
order.getStatus();
|
||||
}
|
||||
|
||||
Assert.assertEquals("same rc to ids.size() ", rc, ids.size());
|
||||
Assert.assertEquals("same rc to list.size()", rc, list.size());
|
||||
assertEquals("same rc to ids.size() ", rc, ids.size());
|
||||
assertEquals("same rc to list.size()", rc, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void find_count_distinct_singleProperty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.setDistinct(true)
|
||||
.select("anniversary")
|
||||
.where().eq("status", Customer.Status.NEW)
|
||||
.query();
|
||||
|
||||
int count = query.findCount();
|
||||
|
||||
assertThat(sqlOf(query)).contains("select count(distinct t0.anniversary) from o_customer t0 where t0.status = ?");
|
||||
assertThat(count).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void find_count_distinct_multipleProperties() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.setDistinct(true)
|
||||
.select("anniversary, status");
|
||||
|
||||
int count = query.findCount();
|
||||
|
||||
assertThat(sqlOf(query)).contains("select count(*) from ( select distinct t0.anniversary, t0.status from o_customer t0)");
|
||||
assertThat(count).isGreaterThan(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user