diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 093d6933c6..607033deb1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -238,12 +238,14 @@ CQueryRowCount buildRowCountQuery(OrmQueryRequest request) { query.setMaxRows(0); boolean countDistinct = query.isDistinct(); + boolean useColumnAlias = selectCountWithColumnAlias; boolean withAgg = false; if (!countDistinct) { withAgg = includesAggregation(request, query); if (!withAgg) { // minimise select clause for standard count query.setSelectId(); + useColumnAlias = false; } } @@ -256,7 +258,7 @@ CQueryRowCount buildRowCountQuery(OrmQueryRequest request) { } predicates.prepare(true); - SqlTree sqlTree = createSqlTree(request, predicates, selectCountWithColumnAlias && withAgg); + SqlTree sqlTree = createSqlTree(request, predicates, useColumnAlias); if (SpiQuery.TemporalMode.CURRENT == query.temporalMode()) { sqlTree.addSoftDeletePredicate(query); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java index 7e5df0f3f2..3fd2f79221 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java @@ -295,9 +295,8 @@ private void appendColumnAlias() { if (useColumnAlias) { sb.append(' '); sb.append(columnAliasPrefix); - sb.append(columnIndex); + sb.append(columnIndex++); } - columnIndex++; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 72a31e8986..d29227b643 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -47,7 +47,7 @@ public final class SqlTreeBuilder { private final boolean disableLazyLoad; private final boolean readOnly; private final SpiQuery.TemporalMode temporalMode; - private SqlTreeNode rootNode; + private final SqlTreeNode rootNode; private boolean sqlDistinct; private final boolean platformDistinctNoLobs; private final SqlTreeCommon common; @@ -73,6 +73,7 @@ public final class SqlTreeBuilder { this.alias = null; this.ctx = null; this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, readOnly, null); + this.rootNode = buildRootNode(desc); } /** @@ -100,12 +101,13 @@ public final class SqlTreeBuilder { this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode); this.distinctOnPlatform = builder.isPlatformDistinctOn(); this.platformDistinctNoLobs = builder.isPlatformDistinctNoLobs(); + this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, readOnly, includeJoin); + this.rootNode = buildRootNode(desc); String fromForUpdate = builder.fromForUpdate(query); CQueryHistorySupport historySupport = builder.historySupport(query); CQueryDraftSupport draftSupport = builder.draftSupport(query); - String colAlias = subQuery ? null : columnAliasPrefix; + String colAlias = subQuery || rootNode.isSingleProperty() ? null : columnAliasPrefix; this.ctx = new DefaultDbSqlContext(alias, colAlias, historySupport, draftSupport, fromForUpdate); - this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, readOnly, includeJoin); } /** @@ -113,7 +115,6 @@ public final class SqlTreeBuilder { */ public SqlTree build() { // build the appropriate chain of SelectAdapter's - buildRoot(desc); // build the actual String String distinctOn = null; String selectSql = null; @@ -214,8 +215,8 @@ private String buildFromClause() { return ctx.content(); } - private void buildRoot(STreeType desc) { - rootNode = buildSelectChain(null, null, desc, null); + private SqlTreeNode buildRootNode(STreeType desc) { + SqlTreeNode root = buildSelectChain(null, null, desc, null); if (!rawSql) { alias.addJoin(queryDetail.getFetchPaths(), desc); alias.addJoin(predicates.predicateIncludes(), desc); @@ -224,6 +225,7 @@ private void buildRoot(STreeType desc) { alias.buildAlias(); predicates.parseTableAlias(alias); } + return root; } /** diff --git a/ebean-test/src/test/java/org/tests/query/TestRowCount.java b/ebean-test/src/test/java/org/tests/query/TestRowCount.java index b65ce9591a..a9f8e5400e 100644 --- a/ebean-test/src/test/java/org/tests/query/TestRowCount.java +++ b/ebean-test/src/test/java/org/tests/query/TestRowCount.java @@ -14,10 +14,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -public class TestRowCount extends BaseTestCase { +class TestRowCount extends BaseTestCase { @Test - public void test() { + void test() { ResetBasicData.reset(); LoggedSql.start(); @@ -46,7 +46,7 @@ public void test() { } @Test - public void find_count_distinct_singleProperty() { + void find_count_distinct_singleProperty() { ResetBasicData.reset(); Query query = DB.find(Customer.class) @@ -57,13 +57,13 @@ public void find_count_distinct_singleProperty() { int count = query.findCount(); - assertThat(sqlOf(query)).contains("select count(distinct t0.anniversary) from o_customer t0 where t0.status = ?"); + // never see column alias used with count distinct on single column + assertThat(query.getGeneratedSql()).contains("select count(distinct t0.anniversary) from o_customer t0 where t0.status = ?"); assertThat(count).isGreaterThan(0); } @Test - public void find_count_distinct_multipleProperties() { - + void find_count_distinct_multipleProperties() { ResetBasicData.reset(); Query query = DB.find(Customer.class) @@ -72,7 +72,32 @@ public void find_count_distinct_multipleProperties() { int count = query.findCount(); - assertThat(sqlOf(query)).contains("select count(*) from ( select distinct t0.anniversary, t0.status from o_customer t0)"); + if (isSqlServer() || isH2() || isMariaDB() || isMySql()) { + assertThat(query.getGeneratedSql()).contains("select count(*) from ( select distinct t0.anniversary c0, t0.status c1 from o_customer t0)"); + } else { + assertThat(query.getGeneratedSql()).contains("select count(*) from ( select distinct t0.anniversary, t0.status from o_customer t0)"); + } + assertThat(count).isGreaterThan(0); + } + + @Test + void find_count_distinct_multiplePropertiesSameColumn() { + ResetBasicData.reset(); + + Query query = DB.find(Customer.class) + .setDistinct(true) + .select("id, anniversary, status") + .fetch("billingAddress", "id, city") + .fetch("shippingAddress", "id, city"); + + int count = query.findCount(); + + if (isSqlServer() || isH2() || isMariaDB() || isMySql()) { + // must use column alias + assertThat(query.getGeneratedSql()).contains("select count(*) from ( select distinct t0.anniversary c0, t0.status c1, t1.id c2, t1.city c3, t2.id c4, t2.city c5 from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id left join o_address t2 on t2.id = t0.shipping_address_id)"); + } else { + assertThat(query.getGeneratedSql()).contains("select count(*) from ( select distinct t0.anniversary, t0.status, t1.id, t1.city, t2.id, t2.city from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id left join o_address t2 on t2.id = t0.shipping_address_id)"); + } assertThat(count).isGreaterThan(0); } diff --git a/platforms/h2/src/main/java/io/ebean/platform/h2/H2Platform.java b/platforms/h2/src/main/java/io/ebean/platform/h2/H2Platform.java index b879b2c039..b8f2150c85 100644 --- a/platforms/h2/src/main/java/io/ebean/platform/h2/H2Platform.java +++ b/platforms/h2/src/main/java/io/ebean/platform/h2/H2Platform.java @@ -24,6 +24,7 @@ public H2Platform() { this.dbEncrypt = new H2DbEncrypt(); this.historySupport = new H2HistorySupport(); this.nativeUuidType = true; + this.selectCountWithColumnAlias = true; this.supportsDeleteTableAlias = true; this.inlineSqlUpdateLimit = true; this.dbDefaultValue.setNow("now()"); diff --git a/platforms/mariadb/src/main/java/io/ebean/platform/mariadb/MariaDbPlatform.java b/platforms/mariadb/src/main/java/io/ebean/platform/mariadb/MariaDbPlatform.java index 3b7ecd52f0..c21b6624fc 100644 --- a/platforms/mariadb/src/main/java/io/ebean/platform/mariadb/MariaDbPlatform.java +++ b/platforms/mariadb/src/main/java/io/ebean/platform/mariadb/MariaDbPlatform.java @@ -16,6 +16,7 @@ public MariaDbPlatform() { super(); this.platform = Platform.MARIADB; this.sequenceBatchMode = false; + this.selectCountWithColumnAlias = true; // for MariaDB probably turn off forwardOnlyHintOnFindIterate with later driver // this.forwardOnlyHintOnFindIterate = false; this.historySupport = new MariaDbHistorySupport(); diff --git a/platforms/mysql/src/main/java/io/ebean/platform/mysql/BaseMySqlPlatform.java b/platforms/mysql/src/main/java/io/ebean/platform/mysql/BaseMySqlPlatform.java index 18f3794bc9..eb3b03f729 100644 --- a/platforms/mysql/src/main/java/io/ebean/platform/mysql/BaseMySqlPlatform.java +++ b/platforms/mysql/src/main/java/io/ebean/platform/mysql/BaseMySqlPlatform.java @@ -13,6 +13,7 @@ public abstract class BaseMySqlPlatform extends DatabasePlatform { public BaseMySqlPlatform() { super(); this.useExtraTransactionOnIterateSecondaryQueries = true; + this.selectCountWithColumnAlias = true; this.selectCountWithAlias = true; this.supportsSavepointId = false; this.inlineSqlUpdateLimit = true;