【问题标题】:How can I replicate 'PARTITION BY' on DocumentDb如何在 DocumentDb 上复制“PARTITION BY”
【发布时间】:2016-03-16 18:34:59
【问题描述】:

我需要找到每个人最后一次登录的记录。这适用于 T-SQL

SELECT *
FROM
(
  SELECT lh.*, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY DateCreated DESC) AS RowNumber
  FROM LoginHistory lh
) lhp
where lhp.RowNumber = 1

但由于 DocumentDb 减少了feature set,我不知道如何处理。

我认为这需要作为存储过程来解决,但我也不知道如何构建它。循环多个异步调用? (甚至不知道该怎么做。)下载所有记录并仅使用 JS 过滤它们?

如何翻译?

更新:示例输出

PARTITION BY 类似于​​ GROUP BY,但它不是聚合结果,而是将记录视为一种范围。所以

  SELECT lh.*, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY DateCreated DESC) AS RowNumber
  FROM LoginHistory lh

会返回类似的东西

UserId    DateCreated            RowNumber
1         2015-12-10 22:44:03    1
1         2015-12-10 13:35:12    2
1         2015-12-09 18:52:25    3
2         2015-12-10 20:53:13    1
2         2015-12-10 08:12:41    2

它基本上说,“对于给定的 UserId,按 DateCreated 对这些记录进行排序”。

然后我只需在外部查询中选择 RowNumber = 1,我就有每个用户的最新记录。

我的问题使用了 SQL 2015+ 语法,但也可以使用 SQL 2005 之前的语法,这可以通过 this 之类的东西来完成:

Select Date, User, Status, Notes 
from [SOMETABLE]
inner join 
(
    Select max(Date) as LatestDate, [User]
    from [SOMETABLE]
    Group by User
) SubMax 
on [SOMETABLE].Date = SubMax.LatestDate
and [SOMETABLE].User = SubMax.User 

很遗憾,DocumentDb 也不支持 GroupBy。

【问题讨论】:

  • 我不熟悉 OVER ... PARTITION BY。你能提供一个示例输出吗?我是 documentdb-lumenize 的作者,它是一个在 DocumentDB 中作为存储过程运行的聚合库。您指定 groupBy 甚至多维立方体,所以我怀疑它可以满足您的需求,但我需要这个例子来确定。
  • 感谢您的更新。我现在了解到您正在寻找最新的。我将在下面发布答案。
  • 我在下面的回答有帮助吗?你走哪条路?

标签: azure azure-cosmosdb


【解决方案1】:

您似乎想要给定 UserId 的最新值。我可以想到三种替代方法,您建议使用第二种方法:

  1. 将所有内容取回给客户端并提取您想要的行。可能会占用带宽并存在延迟问题,具体取决于数据的大小。

  2. 编写存储过程。如果这是您想要做的很多操作并且性能很关键,那么您可能希望采用这种方法。我能够快速调整我的 countDocuments sproc 以创建您想要的 sproc。你可以找到它here(更新:添加了tests 并修复了我第一次发布时的错误)。如果您使用的是 .NET,则需要预编译该存储过程(用 CoffeeScript 编写)并将其发送到您的服务器。如果您使用的是 node.js,我建议使用 documentdb-utils.loadSprocs 从特定目录加载所有存储过程。 loadSprocs 负责编译,甚至允许您通过实现require() 支持来模块化和使用存储过程中的 npm 包。

  3. 如果您希望进行其他聚合或分区,那么我推荐一个更通用的解决方案,documentdb-lumenize,它是在 DocumentDB 中的存储过程中运行的聚合“库”。您使用配置调用存储过程以指定要执行的“聚合”(或分区)。该答案的其余部分是关于如何使用 documentdb-lumenize 执行此操作的描述。

firstValue(如果您按 DESC 排序)或 lastValue(如果您使用默认 ASC)聚合函数就是您想要的。

假设您使用的是 node.js/CoffeeScript,我将给出答案,但您可以使用 JavaScript 和/或 .NET 进行等效操作。我可以根据您的喜好为您制作一个完整的示例。请告诉我。

第一步是您的查询:

filterQuery = "SELECT c.ValueToReturn FROM c ORDER BY c.DateCreated"

接下来,您将定义要“分区”的字段

dimensions = [{field: "UserId"}]

然后,您需要定义“指标”。

metrics = [{field: 'ValueToReturn', f: "lastValue", as: "Last Value"}]

最后,将所有内容捆绑到一个配置对象中

cubeConfig = {dimensions, metrics}
config = {cubeConfig, filterQuery}

然后,当您调用“立方体”存储过程时,将配置作为唯一参数发送。有关如何将多维数据集放入您的集合并执行它的示例,请参见 documentdb-lumenize 文档(提供了 CoffeeScript/JavaScript/.NET 示例)。

我有一个基于浏览器的 Lumenize 实现,我可以给你一个JSFiddle working example。唯一的区别是我没有为浏览器配置指定 filterQuery。由于 documentdb-lumenize 在 DocumentDB 中工作,因此它需要这些基于浏览器不需要的额外信息。它也与上面的示例略有不同,因为我坚持使用刚刚剪切和粘贴的示例数据中的 DESC 排序顺序。你想要的那个是“firstValue”。如果排序顺序是 ASC,那么您需要“lastValue”,这就是我在上面显示的内容。

注意,上面的例子只会返回一个字段(这里显示为“ValueToReturn”。如果你想要更多,你需要确保它们在你的 SELECT 子句中(或者只是说“*”)并且您需要为指标表中的每个字段设置一行。您可以以编程方式构建指标表,因此即使您需要 50 个字段也只需几行代码。如果您需要 CoffeeScript/JavaScript,请告诉我如何以编程方式执行此操作的示例。

【讨论】:

  • 我选择了选项 2。
猜你喜欢
  • 2019-09-16
  • 2013-10-11
  • 2011-09-06
  • 1970-01-01
  • 2020-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多