0

我正在尝试从 /ExampleRegion 查询所有记录的 ID。如果 ID 的计数仅为 1,我想检索记录,因此该 ID 的区域中只有 1 条记录。

SELECT COUNT(*), id from /ExampleRegion group by id --> 仅当该 id 的计数为 1 时。

如何在 WHERE 条件中使用 COUNT 作为条件?

我尝试了以下方法,但它不起作用:

SELECT * from /ExampleRegion a where (SELECT count(*) as c, b.id from /ExampleRegion b where b.id = a.id and c = 1)

SELECT * from /ExampleRegion a where (SELECT count(*) as c from /ExampleRegion b where b.id = a.id ) = 1

我认为 GROUP BY 会起作用,尽管我似乎仍然找不到正确的 OQL。

非常感激。

4

1 回答 1

1

注意:为了帮助解决和(尝试)解决这个问题,我创建了一个测试类以及一个简单的User 应用程序域模型类来将此问题置于上下文中。

简而言之...

关于...

如何在 WHERE 条件中使用 COUNT 作为条件?

您不能像countWHERE子句的谓词中那样使用聚合 OQL 查询函数(我怀疑您已经发现),例如:

SELECT x.id, count(*) AS cnt FROM /Users x WHERE count(*) = 1 GROUP BY x.id

这会导致以下异常:

Caused by: org.apache.geode.cache.query.QueryInvalidException: Aggregate functions can not be used as part of the WHERE clause.
    at org.apache.geode.cache.query.internal.QCompiler.checkWhereClauseForAggregates(QCompiler.java:204)
    at org.apache.geode.cache.query.internal.QCompiler.checkWhereClauseForAggregates(QCompiler.java:214)
    at org.apache.geode.cache.query.internal.QCompiler.select(QCompiler.java:260)
...

此外,不幸的是,以下 OQL 查询:

SELECT x.id, count(*) AS cnt FROM /Users x WHERE cnt = 1 GROUP BY x.id

不返回任何结果!

用于查找重复项的相反 OQL 查询也不返回任何结果:

SELECT x.id, count(*) AS cnt FROM /Users x WHERE cnt = 1 GROUP BY x.id

虽然,我不完全确定为什么,但我怀疑这是由于与上面第一个 OQL 查询相同的限制,在子句count内的 OQL 查询谓词中使用了聚合函数WHERE,除了后面的形式信息量较少(例如,像我怀疑它可能在某处吃异常,因为根据 GemFire,OQL 查询在语法上是正确的)。

但是,如果您只关心 ID,那么您可以简单地运行类似的 OQL 查询:

SELECT x.id, count(*) AS cnt FROM /Users x GROUP BY x.id

当然,这个 OQL 查询返回一个投影(或 GemFire Struct( Javadoc )),它返回所有用户 ID(重复和唯一)的计数。显然,如果用户 ID 的计数为 1,则它是唯一的,如果大于 1,则重复(即不唯一)。

详细地...

User但是,通常情况下,当User实例具有唯一 ID(在您的情况下)或重复 ID时,用户希望访问实际对象(例如)。用户这样做是为了对 OQL 查询返回的 Region 条目值(例如 )执行一些操作User,这在用于以并行和分布式方式处理 PARTITION Regions 的函数中特别常见。

但是,我不得不承认,我对无法(完全)解决这个问题有点傻眼。

老实说,我认为这个问题应该可以通过以下 GemFire OQL 查询来解决:

SELECT u 
FROM /Users u, (SELECT DISTINCT x.id AS id, count(*) AS cnt FROM /Users x GROUP BY x.id) v
WHERE v.cnt = 1 
AND u.id = v.id 
ORDER BY u.name ASC

本质上,这个 OQL 查询会选择所有Users他们的 ID 唯一的地方,因为它们是同类中的 1 个。

奇怪的是,这会导致 GemFire QueryInvalidException

org.springframework.data.gemfire.GemfireQueryException: ; nested exception is org.apache.geode.cache.query.QueryInvalidException: 

    at org.springframework.data.gemfire.GemfireCacheUtils.convertGemfireAccessException(GemfireCacheUtils.java:303)
    at org.springframework.data.gemfire.GemfireCacheUtils.convertQueryExceptions(GemfireCacheUtils.java:325)
    at org.springframework.data.gemfire.GemfireAccessor.convertGemFireQueryException(GemfireAccessor.java:109)
    at org.springframework.data.gemfire.GemfireTemplate.find(GemfireTemplate.java:326)
    at org.springframework.data.gemfire.repository.query.StringBasedGemfireRepositoryQuery.execute(StringBasedGemfireRepositoryQuery.java:159)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:135)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:119)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:151)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
    at io.stackoverflow.questions.apache.geode.query.$Proxy45.findUsersWithDuplicateId(Unknown Source)
    at io.stackoverflow.questions.apache.geode.query.QueryCountEqualToOneIntegrationTests.duplicateCountQueryIsCorrect(QueryCountEqualToOneIntegrationTests.java:112)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.apache.geode.cache.query.QueryInvalidException: 
    at org.apache.geode.cache.query.internal.DefaultQuery.<init>(DefaultQuery.java:172)
    at org.apache.geode.cache.query.internal.DefaultQueryService.newQuery(DefaultQueryService.java:150)
    at org.springframework.data.gemfire.GemfireTemplate.find(GemfireTemplate.java:313)
    ... 43 more
Caused by: org.apache.geode.cache.query.TypeMismatchException: Exception in evaluating the Collection Expression in getRuntimeIterator() even though the Collection is independent of any RuntimeIterator
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.evaluateCollectionForIndependentIterator(CompiledIteratorDef.java:143)
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.getRuntimeIterator(CompiledIteratorDef.java:117)
    at org.apache.geode.cache.query.internal.CompiledSelect.computeDependencies(CompiledSelect.java:189)
    at org.apache.geode.cache.query.internal.DefaultQuery.<init>(DefaultQuery.java:170)
    ... 45 more
Caused by: java.lang.NullPointerException
    at org.apache.geode.cache.query.internal.CompiledSelect.applyProjectionAndAddToResultSet(CompiledSelect.java:1309)
    at org.apache.geode.cache.query.internal.CompiledSelect.doNestedIterations(CompiledSelect.java:800)
    at org.apache.geode.cache.query.internal.CompiledSelect.doNestedIterations(CompiledSelect.java:844)
    at org.apache.geode.cache.query.internal.CompiledSelect.doIterationEvaluate(CompiledSelect.java:703)
    at org.apache.geode.cache.query.internal.CompiledSelect.evaluate(CompiledSelect.java:426)
    at org.apache.geode.cache.query.internal.CompiledGroupBySelect.evaluate(CompiledGroupBySelect.java:157)
    at org.apache.geode.cache.query.internal.CompiledGroupBySelect.evaluate(CompiledGroupBySelect.java:42)
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.evaluateCollection(CompiledIteratorDef.java:184)
    at org.apache.geode.cache.query.internal.RuntimeIterator.evaluateCollection(RuntimeIterator.java:104)
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.evaluateCollectionForIndependentIterator(CompiledIteratorDef.java:128)
    ... 48 more

软件中没有什么比 NPE 更让我恼火的了!它们是一个明显且存在的程序员错误;不是用户错误!

看起来,GemFire 对子句中声明的嵌套 OQL 查询不满意FROM,这实际上会创建一个可查询的集合,或在外部查询中使用的中间结果集(很像 RDBMS 临时表):

TypeMismatchException: Exception in evaluating the Collection Expression in getRuntimeIterator() even though the Collection is independent of any RuntimeIterator

也许,GemFire/Geode 对这个嵌套(临时)集合的“投影”特别不满意,因此这里的 NPE:

Caused by: java.lang.NullPointerException
    at org.apache.geode.cache.query.internal.CompiledSelect.applyProjectionAndAddToResultSet(CompiledSelect.java:1309)
    at org.apache.geode.cache.query.internal.CompiledSelect.doNestedIterations(CompiledSelect.java:800)

当我查看受影响的 GemFire/Geode代码时,确切的条件对我来说真的没有意义,因为我ClientCache使用LOCAL(仅)区域进行了测试。#叹

尽管如此,我什至尝试Cache使用区域(启用 PDX(实际上是 PR 需要))使用对等实例进行测试,PARTITION结果相同!#叹

count鉴于 GemFire 查询引擎似乎在嵌套 OQL 查询(包含and子句)的投影中遇到问题,GROUP BY我决定尝试向查询引擎提供更多信息,以期更好地告知查询引擎有关投影值的信息。因此,我创建了UserIdCount 投影类类型并在我的 OQL 查询中使用它,如下所示:

IMPORT io.stackoverflow.questions.spring.geode.app.model.UserIdCount;
SELECT DISTINCT u 
FROM /Users u, (SELECT DISTINCT x.id AS id, count(*) AS cnt FROM /Users x GROUP BY x.id) v TYPE UserIdCount
WHERE v.cnt = 1 
AND u.id = v.id 
ORDER BY u.name ASC

当然,不幸的是,这也没有达到预期的效果,只会导致以下异常:

java.lang.IllegalArgumentException: element type must be struct

    at org.apache.geode.cache.query.internal.StructSet.setElementType(StructSet.java:365)
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.prepareIteratorDef(CompiledIteratorDef.java:275)
    at org.apache.geode.cache.query.internal.CompiledIteratorDef.evaluateCollection(CompiledIteratorDef.java:200)
    at org.apache.geode.cache.query.internal.RuntimeIterator.evaluateCollection(RuntimeIterator.java:104)
    at org.apache.geode.cache.query.internal.CompiledSelect.doNestedIterations(CompiledSelect.java:813)
...

看来我被一个 GemFire 卡住了Struct,您认为 GemFire 在访问外部查询中的投影值时会知道如何在嵌套查询中处理它。但是无所谓!

我真的觉得 NPE 是 GemFire 的意外结果,并且 GemFire 真的应该能够(并且可能能够)处理这种类型的 OQL 查询。

那么,你还剩下什么。

好吧,正如我上面所说,如果您只关心 ID,那么您可以返回所有 ID 及其计数,并迭代 Struts 列表以找到计数为 1 的 ID。

当然,如果您最终对具有唯一(或可能重复)ID 的对象感兴趣以执行额外的处理,那么您需要将其分解为 2 个单独且单独的 OQL 查询,首先获取感兴趣的 ID,然后使用这些 IDUsers在另一个查询中获取对象/值(例如)。

在这个测试用例中,我已经为您的用例(即唯一 ID)演示了这种两阶段查询方法。

无论如何,我希望这能给你一些选择或思考的事情。

干杯!

于 2021-02-13T05:19:11.080 回答