JPQL / QueryDSL:加入子查询并获取别名列

发布于 2024-11-28 01:30:58 字数 1801 浏览 3 评论 0原文

我试图通过加入子查询来获取 groupBy 计数的平均值。不知道这是否是正确的方法,但除了 mysema 文档之外,我对子查询一无所知。

设想: 客户平均每种产品有多少订单? 含义:客户订购产品。因此,客户多次(计数)订购特定产品。客户对任何产品下的平均订单数是多少?

听起来可能有点假设,事实上它只是原型的一部分,但它让我想知道如何使用 Mysema 的精美 QueryDSL 获取对在子查询中创建的自定义列的引用。

在 SQL 中,您只需为计数列指定一个别名,然后使用第二个 ID 列进行连接。 QueryDSL 也有“as()”方法,但我不知道如何检索该列,而且我不知道它如何将一个查询与另一个查询连接起来,因为 query.list() 只是获取一个列表,但对于某些连接接受它的原因。感觉不对......

这是我的代码:

    JPQLQuery query = createJPQLQuery();

    QOrdering qOrdering = QOrdering.ordering;
    QProduct qProduct = QProduct.product;
    QCustomer qCustomer = QCustomer.customer;           

    // how many of each product did a customer order?
    HibernateSubQuery subQuery = new HibernateSubQuery();
    subQuery.from(qOrdering).innerJoin(qOrdering.product,qProduct).innerJoin(qOrdering.customer, qCustomer);
    subQuery.groupBy(qCustomer,qProduct).list(qCustomer.id,qProduct.id,qProduct.count());

    // get the average number of orders per product for each customer
    query.from(qCustomer);      
    query.innerJoin(subQuery.list(qCustomer.id,qOrdering.count().as("count_orders")));      
    query.groupBy(qCustomer.id);
    return (List<Object[]>) query.list(qCustomer.firstname,subQuery.count_orders.avg());

再次:如何加入子查询? 如何获得别名“count”列以进行更多聚合,例如 avg(顺便说一句,我的组正确吗?) 可能我还有其他一些错误,所以任何帮助表示赞赏!

谢谢!

编辑: 这就是我希望 QueryDSL 生成的原生 SQL:

Select avg(numOrders) as average, cust.lastname from
customer cust
inner join
(select count(o.product_id) as numOrders, c.id as cid, p.name
from ordering o
inner join product p on o.product_id=p.id
inner join customer c on o.customer_id=c.id
group by o.customer_id, o.product_id) as numprods
on cust.id = numprods.cid
group by numprods.cid
order by cust.lastname;

I'm trying to get an average for a count on a groupBy by joining with a subquery. Don't know if that the right way to go at all but I couldn't anything about subqueries other than the mysema doc.

Scenario:
How many orders per product did a customer do on average?
Meaning: A Customer orders products. So a customer ordered a specific product a number of times (count). What's the average number of orders that customer placed for any product?

Might sound a bit hypothetical, in fact it's just part of a prototype, but it made me wonder, how to get a reference to a custom column created within a subquery with the fancy QueryDSL from Mysema.

In SQL you just give the count column an alias and join using a second ID column. QueryDSL has the "as()" method as well but I have no Idea, how to retrieve that column plus I dont't see how it can join one query with anothers, since query.list() just gets a list but for some reason the join accepts it. Feels wrong...

Here's my code:

    JPQLQuery query = createJPQLQuery();

    QOrdering qOrdering = QOrdering.ordering;
    QProduct qProduct = QProduct.product;
    QCustomer qCustomer = QCustomer.customer;           

    // how many of each product did a customer order?
    HibernateSubQuery subQuery = new HibernateSubQuery();
    subQuery.from(qOrdering).innerJoin(qOrdering.product,qProduct).innerJoin(qOrdering.customer, qCustomer);
    subQuery.groupBy(qCustomer,qProduct).list(qCustomer.id,qProduct.id,qProduct.count());

    // get the average number of orders per product for each customer
    query.from(qCustomer);      
    query.innerJoin(subQuery.list(qCustomer.id,qOrdering.count().as("count_orders")));      
    query.groupBy(qCustomer.id);
    return (List<Object[]>) query.list(qCustomer.firstname,subQuery.count_orders.avg());

Again: How do I join with a subquery?
How do I get the aliased "count" column to do more aggregation like avg (is my group right btw?)
Might be that I have some other errors in this, so any help appreciated!

Thanks!

Edit:
That's kind of the native SQL I'd like to see QueryDSL produce:

Select avg(numOrders) as average, cust.lastname from
customer cust
inner join
(select count(o.product_id) as numOrders, c.id as cid, p.name
from ordering o
inner join product p on o.product_id=p.id
inner join customer c on o.customer_id=c.id
group by o.customer_id, o.product_id) as numprods
on cust.id = numprods.cid
group by numprods.cid
order by cust.lastname;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逐鹿 2024-12-05 01:30:58

不允许在 join 子句中使用子查询。在JPQL中,子查询只允许出现在WHERE和HAVING部分。 Querydsl JPA 查询中的连接方法签名太宽。

由于此查询需要两级分组,因此可能无法使用 JPQL / Querydsl JPA 来表达。

我建议使用 Querydsl JPA Native 查询支持来编写此查询。

由于Querydsl JPA内部使用JPQL,因此受到JPQL表达能力的限制。

Using subqueries in the join clause is not allowed. in JPQL, subqueries are only allowed in the WHERE and HAVING part. The join method signatures in Querydsl JPA queries are too wide.

As this query needs two levels of grouping, maybe it can't be expressed with JPQL / Querydsl JPA.

I'd suggest to write this query using the Querydsl JPA Native query support.

As Querydsl JPA uses JPQL internally, it is restricted by the expressiveness of JPQL.

晚雾 2024-12-05 01:30:58

我知道这个问题很旧并且已经有一个公认的答案,但从 这个问题,看来还是困扰着小伙伴们。请参阅我在同一问题中的回答。在join()部分和Expression.path()中使用JoinFlag可以实现子查询的左连接。希望这对某人有帮助。

I know that this question is old and already has an accepted answer, but judging from this question, it seems to still be troubling guys. See my answer in the same question. The use of JoinFlag in the join() section and Expression.path() is able to achieve left-joining a subquery. Hope this helps someone.

新雨望断虹 2024-12-05 01:30:58

QueryDsl 不支持 join 中的 subQuery,但您可以通过以下方式实现:

我们想要实现以下查询:

select A.* from A join (select aid from B group by aid) b on b.aid=A.id;

将视图或 SQL 查询映射到 JPA 实体:

import lombok.Setter;
import org.hibernate.annotations.Subselect;
import org.hibernate.annotations.Synchronize;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Getter
@Setter
@Subselect("select aid from B group by aid")
@Synchronize("B")
public class BGroupByAid {

    @Id
    private Integer aId;
}

然后在类中使用等效的 QueryDSl 实体,就像常规实体一样:

JPAQuery<QAsset> query = new JPAQuery<>(entityManager);  
QBGroupByAid bGroupById = QBGroupByAid.bGroupByAid;

 List<A> tupleOfAssets =
            query.select(A)
        .from(A).innerJoin(bGroupById).on(bGroupById.aId.eq(A.aId))
        .fetchResults()
        .getResults();
        

QueryDsl does not support subQuery in join but you can achieve this via following way:

We wanted to achieve the following query:

select A.* from A join (select aid from B group by aid) b on b.aid=A.id;

Map a View or SQL query to JPA entity:

import lombok.Setter;
import org.hibernate.annotations.Subselect;
import org.hibernate.annotations.Synchronize;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Getter
@Setter
@Subselect("select aid from B group by aid")
@Synchronize("B")
public class BGroupByAid {

    @Id
    private Integer aId;
}

then use the equivalent QueryDSl entity in the class just like the regular entity:

JPAQuery<QAsset> query = new JPAQuery<>(entityManager);  
QBGroupByAid bGroupById = QBGroupByAid.bGroupByAid;

 List<A> tupleOfAssets =
            query.select(A)
        .from(A).innerJoin(bGroupById).on(bGroupById.aId.eq(A.aId))
        .fetchResults()
        .getResults();
        
呆萌少年 2024-12-05 01:30:58

您还可以使用 blazebit,它也支持连接中的子查询。我已经尝试过并且有效。您可以像这样创建 SubQueryExpression fe

SubQueryExpression<Tuple> sp2 = getQueryFactory().select(entity.id,
                        JPQLNextExpressions.rowNumber().over().partitionBy(entity.folId).orderBy(entity.creationDate.desc()).as(rowNumber))
                .from(entity)
                .where(Expressions.path(Integer.class, rowNumber).eq(1));

,然后像这样加入它:

return getBlazeQueryFactory()
                .select(entity1, entity)
                .from(entity1)
                .leftJoin(sp2, entity).on(entity.id.eq(entity1.id)).fetch();

我在这里只是简单的例子。所以也许它没有完全的意义,但也许会有帮助。

也不要混淆,它会在生成的选择中产生并集。这只是为了命名子查询中的列,您可以在此处阅读有关此联合的更好解释:LEFT JOIN SUBQUERY 中的 Blaze-Persistence GROUP BY 与根查询中的 COALESCE

You can also use blazebit which supports also subquery in join. I have try it and it is working. You can create SubQueryExpression f.e like this

SubQueryExpression<Tuple> sp2 = getQueryFactory().select(entity.id,
                        JPQLNextExpressions.rowNumber().over().partitionBy(entity.folId).orderBy(entity.creationDate.desc()).as(rowNumber))
                .from(entity)
                .where(Expressions.path(Integer.class, rowNumber).eq(1));

and then just join it like this:

return getBlazeQueryFactory()
                .select(entity1, entity)
                .from(entity1)
                .leftJoin(sp2, entity).on(entity.id.eq(entity1.id)).fetch();

I have put here just simple example. So maybe it doesn't make a perfect sense but maybe can be helpful.

Also don't be confused it will can produce union in the generated select. This is just for naming columns from subquery you can read better explanation about this union here: Blaze-Persistence GROUP BY in LEFT JOIN SUBQUERY with COALESCE in root query

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文