JPQL 中可以选择 EXISTS() 吗?

发布于 2024-11-25 22:03:08 字数 496 浏览 2 评论 0原文

我正在尝试运行一个查询来检查某些条件是否为真并返回一个简单的布尔结果作为输出。有点棘手的是,其中一个条件是测试一组条件是否没有返回结果。

我目前正在使用 JPA-2.0 和 hibernate 作为我的提供程序,由 MySQL 支持。我已经得到了一个在 MySQL 中运行良好的示例查询,但是当尝试让它在 JPQL 中运行时,它失败了。 MySQL 查询看起来有点像这样:

Select exists(Select statement with criteria) 
  or not exists(Select statement with criteria);

我也使用 CASE 得到了相同的输出,但因为 JPQL 不支持该语句。

无论如何,当我尝试在 JPQL 中使用类似的查询时,我收到错误:

“子树意外结束”

,根据我的理解,这意味着查询中缺少某些内容。有谁知道如何解决它?

I'm trying to run a query that checks if some conditions are true and returns a simple boolean result as output. What makes it slightly tricky is that one of the conditions is to test for whether no results are returned for a set of criteria.

I'm currently using JPA-2.0 with hibernate as my provider, backed by MySQL. I have gotten an example query working fine in MySQL, but when trying to get it running in JPQL it flops. The MySQL query looks a bit like this:

Select exists(Select statement with criteria) 
  or not exists(Select statement with criteria);

I also got the same output using CASE, but as JPQL doesn't support that statement.

Anyways, when I try to use a similar query in JPQL I get the error:

"unexpected end of subtree"

which from my understanding means that something is missing in the query. Does anyone have any idea how to fix it?

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

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

发布评论

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

评论(9

︶ ̄淡然 2024-12-02 22:03:09

DB 不统计所有记录的效率更高。创建本机查询

Spring-Data-JPA

@Query(value = ".....", nativeQuery = true)

或 JPA:

@NamedNativeQuery(name=.., query="..", resultClass=..)

It is more efficient for DB not counting all records. Create native query

Spring-Data-JPA

@Query(value = ".....", nativeQuery = true)

or JPA:

@NamedNativeQuery(name=.., query="..", resultClass=..)
∞琼窗梦回ˉ 2024-12-02 22:03:09

不幸的是,exists 表达式(在 JPA 2.0 中添加)只能在 where 子句中使用。所以你不能直接按照你想要的方式使用它。但是,通过稍微修改语法,您至少可以在一些更简单的用例中使用它。

基本思想是,而不是
选择存在(选择符合条件的内容)
你写
选择存在的常量(选择条件所在的内容),结果由您是否获得(常量)结果行的事实给出。对于常量,我不确定是否有比包含 PK 从任何表中选择的(基本上是虚拟的)更简单的方法。

作为更具体的示例,假设您要检查数据库中是否存在具有指定电子邮件地址的第二个人。你想写一些类似的东西
选择存在(选择 p.id WHERE p.phoneNumber=:phoneNumber 和 p.id<>:primaryPersonId)
其他答案让你写
select count(p.id) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId) (或稍微调整但对性能没有明显影响的 select (count 时的情况) (p.id)>0 then 1 else 0) 其中 p.phoneNumber=:phoneNumber 且 p.id<>:primaryPersonId))
它工作正常,但不必要地计算完整计数,这可能会对数据库产生性能影响(取决于具体情况)。

但你可以写
从 p.id=:primaryPersonId 且存在的人员中选择 p.id(select p.id where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId)
当有重复项时,它将返回一个结果行;当没有重复项时(当 exists 返回 false 时),不返回任何行。

Unfortunately, the exists expression (added in JPA 2.0) can be used only in the where clause. So you cannot use it directly in the way you want. However, with a slight massaging of syntax, you can use it in some simpler usecases, at least.

The basic idea is that instead of
select exists(select something where criteria)
you write
select constant where exists(select something where criteria) and the result is given by the fact whether you got the (constant) result row or not. For the constant, I’m not sure if there is a simpler way than to include a (basically dummy) select by PK from any table.

As a more specific example, imagine you want to check whether there is a second person with the specified e-mail address in the database. You’d like to write something similar to
select exists(select p.id WHERE p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId).
The other answers make you write
select count(p.id) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId) (or the slightly tweaked but without noticeable effect on performance select (case when count(p.id)>0 then 1 else 0) where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId))
which works fine but unnecessarily computes the full count which can have performance impact on the database (depending on the specifics).

But you can write
select p.id from persons where p.id=:primaryPersonId and exists(select p.id where p.phoneNumber=:phoneNumber and p.id<>:primaryPersonId) instead,
which will return one result row when there are duplicates, and returns no rows when there are no duplicates (when the exists returned false).

旧夏天 2024-12-02 22:03:09

解决方案 count(*) > 0 对我来说在 Postgres 中触发了全表扫描:

EXPLAIN ANALYSE select count(*) > 0 from msg where type = 'ALERT';

Node Type   Entity  Cost            Rows    Time        Condition
Aggregate   [NULL]  462793 - 462793 1       24606.407   [NULL]
Gather      [NULL]  462793 - 462793 3       24606.397   [NULL]
Aggregate   [NULL]  461793 - 461793 1       24560.095   [NULL]
Seq Scan    msg     0.00 - 460781   335954  24489.559   ((type)::text = 'ALERT'::text)

正确的性能方法是使用 limittop 关键字进行短路扫描。由于分页不可移植,您必须求助于 setMaxResults()

Query query = EntityManager.createQuery("select 1 from Book where ...", Integer.class);
List<Integer> tinyList = query.setFirstResult(0).setMaxResults(1).getResultList();
if (tinyList.isEmpty()) { ... }

The solution with count(*) > 0 triggered full table scan in Postgres for me:

EXPLAIN ANALYSE select count(*) > 0 from msg where type = 'ALERT';

Node Type   Entity  Cost            Rows    Time        Condition
Aggregate   [NULL]  462793 - 462793 1       24606.407   [NULL]
Gather      [NULL]  462793 - 462793 3       24606.397   [NULL]
Aggregate   [NULL]  461793 - 461793 1       24560.095   [NULL]
Seq Scan    msg     0.00 - 460781   335954  24489.559   ((type)::text = 'ALERT'::text)

The proper performant way is to short-circuit scan with limit or top keyword. As pagination is not portable you have to resort to setMaxResults():

Query query = EntityManager.createQuery("select 1 from Book where ...", Integer.class);
List<Integer> tinyList = query.setFirstResult(0).setMaxResults(1).getResultList();
if (tinyList.isEmpty()) { ... }
陪你搞怪i 2024-12-02 22:03:08

您可以使用 case 表达式< /a>.

从 JPA 2.0 (Java EE 6) 开始,您可以 创建一个 TypedQuery

String query = "select case when (count(*) > 0)  then true else false end from ......"
TypedQuery<Boolean> booleanQuery = entityManager.createQuery(query, Boolean.class);
boolean exists = booleanQuery.getSingleResult();

在 JPA 1.0 (Java EE 5) 中,您必须使用非类型化查询。

Query booleanQuery = entityManager.createQuery(query);
boolean exists = (Boolean) booleanQuery.getSingleResult();

You can do a boolean query using a case expression.

As of JPA 2.0 (Java EE 6) you can create a TypedQuery .

String query = "select case when (count(*) > 0)  then true else false end from ......"
TypedQuery<Boolean> booleanQuery = entityManager.createQuery(query, Boolean.class);
boolean exists = booleanQuery.getSingleResult();

In JPA 1.0 (Java EE 5) you must use an untyped query.

Query booleanQuery = entityManager.createQuery(query);
boolean exists = (Boolean) booleanQuery.getSingleResult();
薄荷港 2024-12-02 22:03:08

这个答案已经过时了。是的,这是可能的。请参考 Rene Link 的正确答案


不,这是不可能的。

请参阅 JPQL BNF 文档来自甲骨文。

simple_cond_expression ::= 比较表达式 |表达式之间 |类似表达式 | in_表达式 |空比较表达式 |空集合比较表达式 |集合成员表达式 | 存在表达式

exists_expression ::= [NOT] EXISTS(子查询)

This answer is obsolete. Yes, it is possible. Please refer to the correct answer from Rene Link


No, it is not possible.

Refer to the JPQL BNF documentation from oracle.

simple_cond_expression ::= comparison_expression | between_expression | like_expression | in_expression | null_comparison_expression | empty_collection_comparison_expression | collection_member_expression | exists_expression

exists_expression ::= [NOT] EXISTS(subquery)

浅浅淡淡 2024-12-02 22:03:08

在一个使用 Hibernate 5.2(支持 JPA 2.1)和 Spring Data Jpa 2.0.6 的项目中,我成功地使用了这个 JPQL 查询:

@Query("SELECT COUNT(c) > 0 FROM Contract c WHERE c.person.id = :pid")
Boolean existContractForPerson(@Param("pid") Long personId);

在日志中,我读到生成的本机查询如下:

select count(contract0_.contract_id)>0 as col_0_0_ from contracts contract0_ where contract0_.fk_person_id=?

In a project with Hibernate 5.2 (which supports JPA 2.1), and Spring Data Jpa 2.0.6, I successfully used this JPQL query:

@Query("SELECT COUNT(c) > 0 FROM Contract c WHERE c.person.id = :pid")
Boolean existContractForPerson(@Param("pid") Long personId);

In the logs, I read that the produced native query is the following:

select count(contract0_.contract_id)>0 as col_0_0_ from contracts contract0_ where contract0_.fk_person_id=?
通知家属抬走 2024-12-02 22:03:08

您的括号不匹配。尝试删除 not 之前的那个(以及第一个存在周围的):

select exists(Select statement with criteria) 
  or not exists(Select statement with criteria);

您不需要将 exists() 括起来的括号

You have mismatched brackets. Try removing the one before the not (and the ones around the first exists):

select exists(Select statement with criteria) 
  or not exists(Select statement with criteria);

You don't need brackets around exists()

梦里人 2024-12-02 22:03:08

或者,您可以使用 select count(...) 并测试它是否返回 0。这应该几乎同样高效,而不需要编写更多代码(事实上,查询本身可能看起来更简单)。

Alternatively you could use a select count(...) and test whether it returns 0. This should be almost as efficient without requiring to write much more code (in fact, the query itself will probably look simpler).

待天淡蓝洁白时 2024-12-02 22:03:08

在一个使用的项目中,

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.2.4.Final</version>
    </dependency>
    ...

我成功地使用了一个

exists(select s.primaryKey from Something s)

子句。所以它可能已经改变了。它也可能是 Hibernate 专有的。由于很多人使用 Hibernate 作为持久性提供程序,我想我可以在这里添加它。

In a project that uses

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.2.4.Final</version>
    </dependency>
    ...

I successfully used an

exists(select s.primaryKey from Something s)

clause. So it might have changed. It could also be Hibernate-proprietary. Since a lot of people use Hibernate as a persistence provider, I thought I might add this here.

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