【问题标题】:Count the number of rows filtered by using "group by" and "having" in JPA计算在JPA中使用“group by”和“have”过滤的行数
【发布时间】:2014-08-13 10:37:12
【问题描述】:

我在 MySQL 数据库中有两个表。

  • 产品
  • order_item

客户下订单的产品存储在order_item 表中 - 从productorder_item 的一对多关系。


目前,我正在执行以下查询。

SELECT t0.prod_id, 
       sum(t1.quantity_ordered) 
FROM   projectdb.product t0, 
       projectdb.order_item t1 
WHERE  (t0.prod_id = t1.prod_id) 
GROUP  BY t0.prod_id 
HAVING (sum(t1.quantity_ordered) >= ?) 
ORDER  BY sum(t1.quantity_ordered) DESC 

产生此 SQL 的条件查询如下。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]>criteriaQuery=criteriaBuilder.createQuery(Object[].class);
Metamodel metamodel = entityManager.getMetamodel();
Root<OrderItem> root = criteriaQuery.from(metamodel.entity(OrderItem.class));

Join<OrderItem, Product> orderItemProdJoin = root.join(OrderItem_.prodId, JoinType.INNER);

List<Expression<?>>expressions=new ArrayList<Expression<?>>();
expressions.add(orderItemProdJoin.get(Product_.prodId));
expressions.add(criteriaBuilder.sum(root.get(OrderItem_.quantityOrdered)));
criteriaQuery.multiselect(expressions.toArray(new Expression[0]));

criteriaQuery.groupBy(orderItemProdJoin.get(Product_.prodId));
criteriaQuery.having(criteriaBuilder.greaterThanOrEqualTo(criteriaBuilder.sum(root.get(OrderItem_.quantityOrdered)), criteriaBuilder.literal(5)));

criteriaQuery.orderBy(criteriaBuilder.desc(criteriaBuilder.sum(root.get(OrderItem_.quantityOrdered))));
List<Object[]> list = entityManager.createQuery(criteriaQuery).getResultList();

此查询汇总order_item 表中每组产品的数量。

它显示如下所示的行列表。

prod_id       qunatity_ordered

 6            11
 8             8
26             8
 7             7
31             7
12             6
27             6
24             5
 9             5

是否可以只计算此查询产生的行数 - 在这种情况下为 9?

我正在使用 EclipseLink 2.5.2 和 Hibernate 4.3.6 final 提供的 JPA 2.1。

【问题讨论】:

    标签: mysql hibernate jpa eclipselink jpa-2.1


    【解决方案1】:

    你有两个选择:

    SELECT COUNT(*)
      FROM (
       SELECT 1, 
         FROM projectdb.product t0, 
              projectdb.order_item t1 
        WHERE (t0.prod_id = t1.prod_id) /* I prefer not to use Implicit Joins */
     GROUP BY t0.prod_id 
       HAVING (sum(t1.quantity_ordered) >= ?) 
           ) groups
    

    或者:

    list.size();
    

    【讨论】:

    • ORM 不支持 FROM 子句中的子查询(直到现在)。
    • 很公平......他们有什么理由不能使用list.size()吗?
    • list.size() 要求将整个列表加载到内存中,当列表太大时,这可能会造成性能瓶颈并可能导致内存泄漏。
    • 它必须是一个超级大的列表,如果你只是SELECT 1(或t0.prod_id)并且不包括ORDER BY,它本质上只是一个整数数组。如果您仍然担心,我相信您的系统一定有运行自定义 SQL 的方法。
    【解决方案2】:

    计算此类行数的一种方法是将给定查询包装在另一个计算行数的查询中,并使给定查询成为子查询,如下所示。

    SELECT count(DISTINCT(t0.prod_id)) 
    FROM   projectdb.product t0 
    WHERE  EXISTS (SELECT t1.prod_id 
                   FROM   projectdb.order_item t2, 
                          projectdb.product t1 
                   WHERE  ((t1.prod_id = t0.prod_id ) 
                            AND ( t1.prod_id = t2.prod_id)) 
                   GROUP  BY t1.prod_id 
                   HAVING (sum(t2.quantity_ordered) >= ?)) 
    

    产生上述 SQL 的条件查询。

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaQuery<Long>criteriaQuery=criteriaBuilder.createQuery(Long.class);
    Metamodel metamodel = entityManager.getMetamodel();
    Root<Product> root = criteriaQuery.from(metamodel.entity(Product.class));
    criteriaQuery.select(criteriaBuilder.countDistinct(root));
    
    Subquery<Long> orderItemSubquery = criteriaQuery.subquery(Long.class);
    Root<OrderItem> orderItemRoot = orderItemSubquery.from(metamodel.entity(OrderItem.class));
    Join<OrderItem, Product> orderItemProdJoin = orderItemRoot.join(OrderItem_.prodId, JoinType.INNER);
    
    orderItemSubquery.select(orderItemProdJoin.get(Product_.prodId));
    orderItemSubquery.where(criteriaBuilder.equal(root, orderItemRoot.get(OrderItem_.prodId)));
    orderItemSubquery.groupBy(orderItemProdJoin.get(Product_.prodId));
    orderItemSubquery.having(criteriaBuilder.greaterThanOrEqualTo(criteriaBuilder.sum(orderItemRoot.get(OrderItem_.quantityOrdered)), criteriaBuilder.literal(5)));
    criteriaQuery.where(criteriaBuilder.exists(orderItemSubquery));
    
    Long count = entityManager.createQuery(criteriaQuery).getSingleResult();
    System.out.println("count = "+count);
    

    我通常避免使用IN() 子查询并使用EXISTS() 子查询。不过,可以使用IN() 重写相同的查询,如下所示。

    SELECT count(DISTINCT(t0.prod_id)) 
    FROM   projectdb.product t0 
    WHERE  t0.prod_id IN (SELECT t1.prod_id 
                          FROM   projectdb.order_item t2, 
                                 projectdb.product t1 
                          WHERE  (t1.prod_id = t2.prod_id) 
                          GROUP  BY t1.prod_id 
                          HAVING (sum(t2.quantity_ordered) >= ?)) 
    

    对应的条件查询。

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaQuery<Long>criteriaQuery=criteriaBuilder.createQuery(Long.class);
    Metamodel metamodel = entityManager.getMetamodel();
    Root<Product> root = criteriaQuery.from(metamodel.entity(Product.class));
    criteriaQuery.select(criteriaBuilder.countDistinct(root));
    
    Subquery<Long> orderItemSubquery = criteriaQuery.subquery(Long.class);
    Root<OrderItem> orderItemRoot = orderItemSubquery.from(metamodel.entity(OrderItem.class));
    Join<OrderItem, Product> orderItemProdJoin = orderItemRoot.join(OrderItem_.prodId, JoinType.INNER);
    
    orderItemSubquery.select(orderItemProdJoin.get(Product_.prodId));
    orderItemSubquery.groupBy(orderItemProdJoin.get(Product_.prodId));
    orderItemSubquery.having(criteriaBuilder.greaterThanOrEqualTo(criteriaBuilder.sum(orderItemRoot.get(OrderItem_.quantityOrdered)), criteriaBuilder.literal(5)));
    criteriaQuery.where(criteriaBuilder.in(root.get(Product_.prodId)).value(orderItemSubquery));
    
    Long count = entityManager.createQuery(criteriaQuery).getSingleResult();
    System.out.println("count = "+count);
    

    关于 ORM 的限制,我找不到比这更好的选择。

    【讨论】:

      猜你喜欢
      • 2011-03-27
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-18
      • 1970-01-01
      相关资源
      最近更新 更多