如何解决GemFire OQL 查询 - 如何在 WHERE 子句中使用 SELECT 语句的计数?
我正在尝试从 /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。
非常感谢。
解决方法
注意:为了帮助解决和(尝试)解决这个问题,我创建了一个 test class 以及一个简单的 User
application domain model class 来将这个问题放到上下文中。
简而言之...
关于...
"如何在 WHERE 条件中使用 COUNT 作为条件?"
您不能在 count
子句的谓词中使用像 WHERE
这样的聚合 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
聚合函数用于 WHERE
内的 OQL 查询谓词子句,除了后面的形式信息较少(例如,我怀疑它可能在某处吃到异常,因为根据 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
实例具有唯一 ID(在您的情况下)或重复 ID 时,用户希望访问实际对象(例如 User
)。用户这样做是为了对 OQL 查询返回的 Region 条目值(例如 User
)执行一些操作,这在用于以并行和分布式方式处理 PARTITION Regions 的 Functions 中尤为常见。
但是,我不得不承认,无法(完全)解决这个问题让我有点傻眼。
老实说,我认为这个问题应该可以通过以下 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 对 FROM
子句中声明的嵌套 OQL 查询不满意,它本质上会创建一个可查询的集合,或在外部查询中使用的中间结果集(很像 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 code 时,确切的条件对我来说真的没有意义,因为我使用 ClientCache
(仅)区域对 LOCAL
进行了测试。 #叹息
尽管如此,我什至尝试使用 Cache
区域(启用 PDX(实际上 PR 需要))对对等 PARTITION
实例进行测试,结果相同! #叹息
鉴于 GemFire 查询引擎在嵌套 OQL 查询(包含 count
和 GROUP BY
子句)的投影中似乎有问题,我决定尝试向查询引擎提供更多信息,希望更好地告知查询引擎有关预计值的信息。因此,我创建了 UserIdCount
projection class type 并在我的 OQL 查询中使用它,如下所示:
IMPORT io.stackoverflow.questions.spring.geode.app.model.UserIdCount;
SELECT DISTINCT u
FROM /Users u,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 ,然后使用这些 ID 在另一个查询中获取对象/值(例如 Users
)。
我已在此 test case 中为您的用例(即唯一 ID)演示了这种两阶段查询方法。
无论如何,我希望这能给你一些选择或考虑的事情。
干杯!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。