我们尝试在Azure Cosmos DB中的查询的 where 子句中使用简单的用户定义函数(UDF),但它无法正常工作。最终目标是查询时间戳_ts
大于或等于昨天的所有结果,但我们的第一步是让UDF正常工作。
缩写数据如下所示:
[
{
"_ts": 1500000007
}
{
"_ts": 1500000005
}
]
使用Azure门户的Cosmos DB 数据资源管理器,如下所示的简单查询将正确返回一个结果:< / p>
SELECT * FROM c WHERE c._ts >= 1500000006
使用我们的UDF 错误的简单查询会返回零结果。此查询如下:
SELECT * FROM c WHERE c._ts >= udf.getHardcodedTime()
该功能的定义如下:
function getHardcodedTime(){
return 1500000006;
}
以下是用于验证的UDF的屏幕截图:
如您所见,两个查询之间的唯一区别是一个查询使用硬编码值,而另一个查询使用UDF获取硬编码值。问题是使用UDF的查询返回零结果而不是返回一个结果。
我们是否正确使用UDF?
更新1
更新UDF以返回数字1
时,我们每次都会得到不同的结果计数。
新功能:
function getHardcodedTime(){
return 1;
}
新查询:SELECT count(1) FROM c WHERE c._ts >= udf.getHardcodedTime()
结果因7240,7236,7233,7264等而异。(此设置是Cosmos DB的实际响应顺序。)
答案 0 :(得分:0)
根据您的描述,最可能的原因是 UDF版本很慢并且返回带有延续令牌而不是最终结果的部分结果。
延续的概念被解释为here:
如果查询的结果不适合单个结果页面,则REST API会通过x-ms-continuation-token响应标头返回延续令牌。客户可以通过在后续结果中包含标题来对结果进行分页。
观察到的计数变化可能是由于查询在稍微不同的时间停止下一页而引起的。检查x-ms-continuation
标题,了解是否属于这种情况。
返回常量的UDF与查询的常量不同。如果您了解得更好,请纠正我,但是从CosmosDB方面来看,它并不知道特定的UDF实际上是确定性的,但假设它可以评估为任何值,因此必须为每个文档执行以检查匹配。这意味着它不能使用索引,必须进行慢速全扫描。
如果您不关心性能,可以继续使用UDF,只需按照继续操作直到处理完所有行。
某些DocumentDB客户端可以为您执行此操作(例如:.net API),因此如果您赶时间,这可能是最快的解决方案。但要注意,这不会扩展(不断变慢,成本越来越高RU),你不应该认为这是一个长期的解决方案。
您可以将hardcodedTime
作为参数传递。通过这种方式,查询执行引擎可以知道该值,可以使用匹配的索引并为您提供正确的结果而无需延续的麻烦。
我不知道您使用的API,但相关阅读以防万一:Parameterized SQL queries。
如果你真的必须控制UDF中的hardcodedTime
,那么你可以实现一个服务器端程序:
hardcodedTime
hardcodedTime
作为参数传递它会使用UDF AND索引,但会带来所需代码量的巨大开销。如果保持UDF值得在开发和维护方面付出额外的努力,那么算一算。
有关SP的相关文档:Azure Cosmos DB server-side programming: Stored procedures, database triggers, and UDFs