Skip to content

Commit

Permalink
Improve count and exists query projections.
Browse files Browse the repository at this point in the history
We now use COUNT(*) and SELECT 1 for count respective exists queries to enable database optimizers instead of using the key columns.

Closes #1647
  • Loading branch information
mp911de committed Nov 6, 2023
1 parent 5312731 commit 9ffbfb6
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,11 @@ public Mono<Long> count(Query query, Class<?> entityClass) throws DataAccessExce

Mono<Long> doCount(Query query, Class<?> entityClass, SqlIdentifier tableName) {

RelationalPersistentEntity<?> entity = getRequiredEntity(entityClass);
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);

StatementMapper.SelectSpec selectSpec = statementMapper //
.createSelect(tableName) //
.doWithTable((table, spec) -> {

Expression countExpression = entity.hasIdProperty()
? table.column(entity.getRequiredIdProperty().getColumnName())
: Expressions.just("1");
return spec.withProjection(Functions.count(countExpression));
});
.withProjection(Functions.count(Expressions.just("*")));

Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
Expand All @@ -290,17 +283,9 @@ public Mono<Boolean> exists(Query query, Class<?> entityClass) throws DataAccess

Mono<Boolean> doExists(Query query, Class<?> entityClass, SqlIdentifier tableName) {

RelationalPersistentEntity<?> entity = getRequiredEntity(entityClass);
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);

StatementMapper.SelectSpec selectSpec = statementMapper.createSelect(tableName).limit(1);
if (entity.hasIdProperty()) {
selectSpec = selectSpec //
.withProjection(entity.getRequiredIdProperty().getColumnName());

} else {
selectSpec = selectSpec.withProjection(Expressions.just("1"));
}
StatementMapper.SelectSpec selectSpec = statementMapper.createSelect(tableName).limit(1)
.withProjection(Expressions.just("1"));

Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void shouldCountBy() {

StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));

assertThat(statement.getSql()).isEqualTo("SELECT COUNT(person.id) FROM person WHERE person.THE_NAME = $1");
assertThat(statement.getSql()).isEqualTo("SELECT COUNT(*) FROM person WHERE person.THE_NAME = $1");
assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
}

Expand Down Expand Up @@ -141,7 +141,7 @@ void shouldProjectCountResultWithoutId() {

MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();

recorder.addStubbing(s -> s.startsWith("SELECT COUNT(1)"), result);
recorder.addStubbing(s -> s.startsWith("SELECT COUNT(*)"), result);

entityTemplate.select(WithoutId.class).count() //
.as(StepVerifier::create) //
Expand All @@ -165,7 +165,7 @@ void shouldExistsByCriteria() {

StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));

assertThat(statement.getSql()).isEqualTo("SELECT person.id FROM person WHERE person.THE_NAME = $1 LIMIT 1");
assertThat(statement.getSql()).isEqualTo("SELECT 1 FROM person WHERE person.THE_NAME = $1 LIMIT 1");
assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void shouldSelectExists() {

StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));

assertThat(statement.getSql()).isEqualTo("SELECT person.id FROM person WHERE person.THE_NAME = $1 LIMIT 1");
assertThat(statement.getSql()).isEqualTo("SELECT 1 FROM person WHERE person.THE_NAME = $1 LIMIT 1");
}

@Test // gh-220
Expand All @@ -239,7 +239,7 @@ void shouldSelectCount() {

StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));

assertThat(statement.getSql()).isEqualTo("SELECT COUNT(person.id) FROM person WHERE person.THE_NAME = $1");
assertThat(statement.getSql()).isEqualTo("SELECT COUNT(*) FROM person WHERE person.THE_NAME = $1");
}

static class Person {
Expand Down

0 comments on commit 9ffbfb6

Please sign in to comment.