如何在 DocumentDb 上复制 'PARTITION BY'

How can I replicate 'PARTITION BY' on DocumentDb

我需要找到每个人最后一次登录的记录。这将适用于 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

会return像

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

它基本上说,"For a given UserId, order those records by DateCreated"。

然后我在外部查询中 select 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。

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

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

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

  3. 如果你希望做其他的聚合或者分区,那么我推荐一个更通用的解决方案,documentdb-lumenize,它是一个聚合"library"运行在一个DocumentDB 中的存储过程。您调用带有配置的存储过程来指定您想要执行的 "aggregation"(或分区)。这个答案的其余部分是关于如何使用 documentdb-lumenize 执行此操作的描述。

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

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

第一步是您的查询:

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

接下来,您将定义您希望"partition by"

的字段
dimensions = [{field: "UserId"}]

然后,您需要定义 "metrics".

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

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

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

然后,在调用 "cube" 存储过程时将配置作为唯一参数发送。有关如何将多维数据集添加到您的集合并执行它的示例,请参阅 documentdb-lumenize 文档(提供了 CoffeeScript/JavaScript/.NET 示例)。

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

请注意,上面的示例只会 return 一个字段(此处显示为 "ValueToReturn"。如果您想要更多,则需要确保它们在您的 SELECT 子句(或只说“*”),您将需要为指标 table 中的每个字段一行。您可以通过编程方式构建指标 table,因此它只会是几个代码行,即使您需要 50 个字段。如果您想要一个 CoffeeScript/JavaScript 如何以编程方式执行此操作的示例,请告诉我。