-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Counting rows with multiple group by clauses - Solved?
PostPosted: Fri Apr 23, 2010 12:11 pm 
Newbie

Joined: Thu Apr 22, 2010 1:46 pm
Posts: 2
I recently ran across a problem that apparently a handful of posters share. Given a Criteria query that mimics the HQL:

Code:
select author.name, publisher.name, year, count(*) from Book group by author.name, publisher.name, year


How do you get a row count? It's a problem for any query with more than one group by clause, and it gets particularly interesting for queries with 3 or more group by clauses. Past solutions have been things like:

  • "wrap the Criteria query's SQL like select count(*) from (criteria-query-sql)" - while really efficient and clean from an SQL perspective, this requires doing gross things with the API.
  • "Criteria.list().size()" - Bad for large resultsets.
  • "Projections.countDistinct(author.name || publisher.name)" - clever, but scales poorly with # of group by clauses.

I think I came up with a generalizable way to do this with a regular old Projection. Apologies if this has been posted already somewhere. The goal of this post is to get feedback and make sure I'm not being retarded, and, if not, help out other people with the same problem.

It turns out you can row-count the above HQL query like so:

Code:
select sum(count(distinct author.name)) from Book group by publisher.name, year


Or, in general, select sum(count(distinct the-first-group-by)) from Foo group by the-rest-of-the-group-bys.

This is pretty easy to do with a custom Projection. Execute-explain said leaving the first group-by clause was harmless and it was easier to code that way, so this deviates slightly from the query above by using the group-by clause verbatim from the original query.

Code:
    public class GroupedRowCount extends SimpleProjection {

        private Projection existingProjection;

        public GroupedRowCount(Projection existingProjection) {
            this.existingProjection = existingProjection;
        }

        @Override
        public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
            return new Type[] { Hibernate.INTEGER };
        }

        @Override
        public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
            StringBuffer result = new StringBuffer();

            if (existingProjection != null && existingProjection.isGrouped()) {
                String existingGroupBy = existingProjection.toGroupSqlString(criteria, criteriaQuery);

                String countMe;
                if (existingGroupBy.contains(",")) {
                    countMe = existingGroupBy.substring(0, existingGroupBy.indexOf(","));
                } else {
                    countMe = existingGroupBy;
                }

                result.append("sum(count(distinct ").append(countMe).append("))");
            } else {
                result.append("count(*)");
            }

            result.append(" as y").append(position).append('_');

            return result.toString();
        }

        @Override
        public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
            return existingProjection.toGroupSqlString(criteria, criteriaQuery);
        }

        @Override
        public boolean isGrouped() {
            return existingProjection != null && existingProjection.isGrouped();
        }

    }


You use it in with a separate criteria like so:

Code:
  Criteria iHaveGroupBys = session.createCriteria(Book.class);
  iHaveGroupBys.setProjection(aNastyProjectionList);
  Criteria rowCounter = session.createCriteria(Book.class);
  rowCoutner.setProjection(new GroupedRowCounter(((CriteriaImpl)iHaveGroupBys).getProjection()));
  Integer rowCount = rowCounter.uniqueResult();


So... thoughts? Is there an infinitely easier way to do this? Can you think of any scenarios in which this is not efficient?


Top
 Profile  
 
 Post subject: Re: Counting rows with multiple group by clauses - Solved?
PostPosted: Fri Mar 23, 2012 4:09 pm 
Newbie

Joined: Mon Feb 13, 2012 6:37 pm
Posts: 2
This doesn't work in MySQL because you can't apply aggregate functions in cascade - i.e. sum(count(. Any other ideas?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.