【问题标题】:User-defined function not returning correct results in Azure Cosmos DB用户定义的函数未在 Azure Cosmos DB 中返回正确的结果
【发布时间】:2018-09-21 10:28:10
【问题描述】:

我们正在尝试在 Azure Cosmos DB 中查询的 where 子句中使用简单的用户定义函数 (UDF),但它无法正常工作。最终目标是查询时间戳_ts 大于或等于昨天的所有结果,但我们的第一步是让 UDF 工作。

缩写数据如下所示:

[
    {
        "_ts": 1500000007
    }
    {
        "_ts": 1500000005
    }
]

使用 Azure 门户的 Cosmos DB 数据资源管理器,类似以下的简单查询将正确返回 一个结果:

SELECT * FROM c WHERE c._ts >= 1500000006

错误地使用我们的 UDF 的简单查询会返回 个结果。该查询如下:

SELECT * FROM c WHERE c._ts >= udf.getHardcodedTime()

函数定义如下:

function getHardcodedTime(){
    return 1500000006;
}

这是用于验证的 UDF 的屏幕截图:

如您所见,两个查询之间的唯一区别是一个查询使用硬编码值,而另一个查询使用 UDF 来获取硬编码值。问题是使用 UDF 的查询返回 zero 结果而不是返回 one 结果。

我们是否正确使用了 UDF?

更新 1 当 UDF 更新为返回数字 1 时,我们每次都会得到不同的结果计数。

新功能:

function getHardcodedTime(){
    return 1;
}

新查询:SELECT count(1) FROM c WHERE c._ts >= udf.getHardcodedTime()

结果因 7240、7236、7233、7264 等而异(这组是 Cosmos DB 响应的实际顺序。)

【问题讨论】:

  • 我没有看到使用 udf 的问题,也无法使用 Cosmos DB Emulator 重现该问题。如果你运行SELECT udf.getHardcodedTime() FROM c,你会得到什么?
  • 我返回了许多对象,其中对象包含来自函数的值。这是一个sn-p。 [ {"$1": 1500000006 }, ... {"$1": 1500000006} ]
  • 会不会是在 select-using-udf 案例中获得了延续令牌?如果 cosmosDB 没有推断出您的 udf 是确定性的,那么它就不能再使用索引,因此它将进行完整扫描。
  • 有没有办法向 Cosmos DB 表明该函数是确定性的?我找到了一些关于如何使用 SQL 数据库执行此操作的示例,但我没有找到任何适用于 Cosmos DB 的示例。
  • 我不认为你可以。但是您可以通过使用 SP,从 UDF 获取参数并将其作为值传递给查询来解决此问题。麻烦,但应该可以。

标签: user-defined-functions azure-cosmosdb


【解决方案1】:

根据您的描述,最可能的原因是UDF 版本速度慢,并返回带有延续令牌的部分结果而不是最终结果

延续的概念在here解释为:

如果查询的结果不能放在单个结果页面中,则 REST API 通过 x-ms-continuation-token 响应标头返回一个继续令牌。客户端可以通过在后续结果中包含标题来对结果进行分页。

观察到的计数变化可能是由于查询在稍微不同的时间停止下一页而引起的。检查x-ms-continuation 标头以了解是否是这种情况。

为什么 UDF 很慢?

返回常量的 UDF 与查询的常量不同。如果您知道得更好,请纠正我,但从 CosmosDB 方面,它不知道特定的 UDF 实际上是确定性的,但假设它可以评估为任何值,因此必须为每个文档执行以检查匹配。这意味着它不能使用索引,必须进行缓慢的全扫描。

你能做什么?

选项 1:继续操作

如果您不关心性能,您可以继续使用 UDF 并继续操作,直到处理完所有行。

一些 DocumentDB 客户端可以为您执行此操作(例如:.net API),因此如果您赶时间,这可能是最快的解决方法。但请注意,这不会扩展(速度越来越慢,并且消耗的 RU 也越来越高),您不应将其视为长期解决方案。

选项 2:删除 UDF,使用参数

您可以将hardcodedTime 作为参数传递。这样,查询执行引擎将知道该值,可以使用匹配索引并为您提供正确的结果,而无需麻烦继续。

我不知道您使用的是哪个 API,但相关阅读以防万一:Parameterized SQL queries

选项 3:包装存储过程

如果您真的必须控制 UDF 中的 hardcodedTime,那么您可以实现一个服务器端过程:

  1. 从UDF查询hardcodedTime
  2. 查询文档,传递hardcodedTime作为参数
  3. 将结果作为 SP 输出返回。

它会使用 UDF AND 索引,但会在所需的代码量方面带来巨大的开销。如果保留 UDF 值得在开发和维护方面付出额外努力,请自行计算。

关于 SP 的相关文档:Azure Cosmos DB server-side programming: Stored procedures, database triggers, and UDFs

【讨论】:

  • 我将此标记为答案,因为我相信您可能是正确的,它是导致此问题的延续令牌,并且您对其他情况有很好的建议。不同之处在于我在 Azure 门户的 Cosmos DB 数据资源管理器中查看结果。让我感到困惑并导致编写此问题的部分是数据资源管理器显示“结果 1 - 1”,计数为零,其中零是不正确的计数。但我们最终发现,您需要继续单击“下一步”按钮并手动汇总您在每个结果集上找到的所有结果。
  • 我刚刚在docs.microsoft.com/en-us/azure/cosmos-db/… 的 Microsoft 文档中找到了一些注释,其中说使用 Azure 门户的数据资源管理器时,可能会通过查询页面返回聚合结果。 “使用 Azure 门户的数据资源管理器时,请注意聚合查询可能会在查询页面上返回部分聚合的结果。SDK 会在所有页面中生成单个累积值。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多