3

How can I implement a count-of-groups in QueryDSL (in Java)?

Background

I'm implementing a paged search on a table, where I want to group the results before returning them.

In addition to the usual LIMIT x OFFSET y query, I also want to receive the total row count.

This is the (simplified) SQL query for one page:

SELECT x, y, MAX(z)
  FROM tasks
 WHERE y > 10
 GROUP BY x, y
 LIMIT 10 OFFSET 0

To retrieve the number of rows, I tried to use a naive COUNT(*) instead of the x, y, MAX(z) like this:

SELECT COUNT(x)
  FROM tasks
 WHERE y > 10
 GROUP BY x, y

Unfortunately, this doesn't produce one row with the number of groups returned by the previous query, but one row for each group, each with the number of rows which were grouped together – as usual for aggregation functions, COUNT changes its meaning when a GROUP BY is present. (At least in Postgresql, what we are using here.)

We can retrieve the total count by using the first SELECT statement as a subselect in a from clause:

SELECT COUNT(*)
FROM ( SELECT x, y, MAX(z)
         FROM tasks
        WHERE y > 10
        GROUP BY x, y
     )

Now the hard part: how can we do this in JPQL and/or QueryDSL?

The first two queries are were produced by code like this:

QTask qTask = QTask.task;
Expression<?>[] groupExps = { qTask.x, qTask.y };
Predicate predicate = qTask.y.gt(10);

List<Result> results = getQueryDSL().createQuery()
                                    .from(qTask)
                                    .where(predicate)
                                    .groupBy(groupExps)
                                    .offset(0)
                                    .limit(10)
                                    .list(ConstructorExpression.create(Result.class,
                                                                       qTask.x
                                                                       qTask.y,
                                                                       qTask.z.max()))
Long total = getQuerydsl().createQuery()
                          .from(qTask)
                          .where(predicate)
                          .groupBy(groupExps)
                          .singleResult(qTask.x.count());

It looks like JPA/JPQL doesn't support subselects other than in WHERE or HAVING-clauses, i.e. they are not possible in the FROM clause. This seems to be the cause that QueryDSL for JPA doesn't support them either.

Is there a way to rewrite this statement, or somehow work around this limitation?

Community
  • 1
  • 1
Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • i am also looking for solution for similar problem like below `select count(*) as count from (select count(stock0_.stock_‌​id) as col_0_0_ from stock stock0_ having sum(stock0_.QTY)>=10‌​0) tbl;` Please let me know if you got the answer. – Anchit Pancholi Aug 16 '16 at 13:43

1 Answers1

2

I can't help you out with jpa and jql, but it's an interesting SQL problem...

What RDMS? The below works in MS SQL Server. You can use concatenation, distinct and count to determine how many unique values there are when you concatenate x and y. This should be the same number of rows when you group by x,y.

select count(distinct CAST(x as char(10)) + CAST(y as char(10)))
from tasks;
KingOfAllTrades
  • 398
  • 1
  • 11
  • Actually, I got a SQL solution, using the subselect in my question. I just have the problem how to tell this to JPA (and QueryDSL). (I'm using Postgresql, by the way.) – Paŭlo Ebermann Sep 05 '14 at 18:49
  • So you are actually concatenating the lines, and then using a "distinct" on the result to see the total count? I'll try if that can be implemented in QueryDSL, and if it is actually faster than just retrieving everything and counting on the client-side. – Paŭlo Ebermann Sep 05 '14 at 18:51
  • Correct. Concatenate, then count only distinct values. I concede this is not an ideal solution, but I just took on the challenge of finding the number of rows of an aggregate query without a subquery in the from clause, which you said is not possible in QueryDSL. Hope it helps in some way. – KingOfAllTrades Sep 05 '14 at 19:43