MongoDB 中带有分区的 sql server 行号,用于返回行的子集

HaB*_*aBo 1 mongodb mongodb-.net-driver

如何使用 MongoDB-Csharp 驱动程序编写以下查询

 SELECT SubSet.*
 FROM   ( SELECT    T.ProductName ,
                    T.Price ,
                    ROW_NUMBER() OVER ( PARTITION BY T.ProductName ORDER BY T.ProductName ) AS ProductRepeat
          FROM      myTable T
        ) SubSet
 WHERE  SubSet.ProductRepeat = 1
Run Code Online (Sandbox Code Playgroud)

我想要实现的是

收藏

  • 产品名称|价格|SKU
  • 帽|10|AB123
  • 包|5|ED567
  • 帽|20|CD345
  • 帽|5|EC123

预期结果是

  • 产品名称|价格|SKU
  • 帽|10|AB123
  • 包|5|ED567

这是一次尝试(请不要使用对象和字段)

public List<ProductOL> Search(ProductOL obj, bool topOneOnly)
        {
            List<ProdutOL> products = new List<ProductOL>();

            var database = MyMongoClient.Instance.OpenToRead(dbName: ConfigurationManager.AppSettings["MongoDBDefaultDB"]);
            var collection = database.GetCollection<RawBsonDocument>("Products");
            List<IMongoQuery> build = new List<IMongoQuery>();

            if (!string.IsNullOrEmpty(obj.ProductName))
            {
                var ProductNameQuery = Query.Matches("ProductName", new BsonRegularExpression(obj.ProductName, "i"));
                build.Add(ProductNameQuery);
            }
            if (!string.IsNullOrEmpty(obj.BrandName))
            {
                var brandNameQuery = Query.Matches("BrandName", new BsonRegularExpression(obj.BrandName, "i"));
                build.Add(brandNameQuery);
            }

            var fullQuery = Query.And(build.ToArray());
            products = collection.FindAs<ProductOL>(fullQuery).SetSortOrder(SortBy.Ascending("ProductName")).ToList();
            if (topOneOnly)
            {
                var tmpProducts = new List<ProductOL>();
                foreach (var item in products)
                {
                    if (tmpProducts.Any(x => x.ProductName== item.ProductName)) { }
                    else
                        tmpProducts.Add(item);
                }
                products = tmpProducts;
            }
            return products;
        }
Run Code Online (Sandbox Code Playgroud)

Ste*_*nie 5

我的 mongo 查询有效并给出了正确的结果。但是当我处理大量数据时,这并不有效,所以我想知道 mongodb 是否有任何像 SQL Server for Row_Number() 和分区这样的概念

如果您的查询返回预期结果但效率不高,您应该使用以下命令查看索引使用情况explain(). 鉴于您的查询生成代码包含条件子句,您似乎可能需要多个索引来有效地覆盖常见变化。

我不确定您提供的 C# 代码与原始 SQL 查询有何关系,因为它们似乎完全不同。除了限制返回的结果之外,我也不清楚分组将如何帮助您的查询性能。

相当于 SQL 查询

MongoDB 中没有直接等效的ROW_NUMBER() .. PARTITION BY分组,但您应该能够使用聚合框架(最快)或Map/Reduce(速度较慢但功能更多)计算出所需的结果。MongoDB 手册包含聚合命令比较以及使用示例。

作为翻译练习,我将重点关注您的 SQL 查询,该查询将按 ProductName 提取第一个产品匹配项:

SELECT SubSet.* FROM ( SELECT T.ProductName , T.Price , ROW_NUMBER() OVER ( PARTITION BY T.ProductName ORDER BY T.ProductName ) AS ProductRepeat FROM myTable T ) SubSet WHERE SubSet.ProductRepeat = 1

设置您提供的测试数据:

db.myTable.insert([
    { ProductName: 'Cap', Price: 10, SKU: 'AB123' },
    { ProductName: 'Bag', Price: 5, SKU: 'ED567' },
    { ProductName: 'Cap', Price: 20, SKU: 'CD345' },
    { ProductName: 'Cap', Price: 5, SKU: 'EC123' },
])
Run Code Online (Sandbox Code Playgroud)

这是 shell 中的聚合查询mongo,它将找到每个组的第一个匹配项(按 ProductName 排序)。MongoCollection.Aggregate()使用方法将该聚合查询转换为 C# 驱动程序应该很简单。

我已在您的原始查询中包含了粗略等效 SQL 片段的注释。

db.myTable.aggregate(
    // Apply a sort order so the $first product is somewhat predictable
    // ( "ORDER BY T.ProductName")
    { $sort: {
        ProductName: 1
        // Should really have additional sort by Price or SKU (otherwise order may change) 
    }},

    // Group by Product Name
    // (" PARTITION BY T.ProductName")
    { $group: {
        _id:   "$ProductName",
        // Find first matching product details per group (can use $$CURRENT in MongoDB 2.6 or list specific fields)
        // "SELECT SubSet.* ... WHERE SubSet.ProductRepeat = 1"
        Price: { $first: "$Price" },
        SKU:   { $first: "$SKU" },
    }},

    // Rename _id to match expected results
    { $project: {
        _id: 0,
        ProductName: "$_id",
        Price: 1,
        SKU: 1,
    }}
)
Run Code Online (Sandbox Code Playgroud)

给出的测试数据的结果似乎是您正在寻找的:

{ "Price" : 10, "SKU" : "AB123", "ProductName" : "Cap" }
{ "Price" : 5, "SKU" : "ED567", "ProductName" : "Bag" }
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 此聚合查询使用$first运算符,因此如果您想查找每个分组的第二个或第三个产品,您需要采用不同的方法(例如,$group然后获取应用程序代码中所需的结果子集)
  • 如果您希望在查找 a 中的第一项时获得可预测的结果$group,则应该有更具体的排序标准ProductName(例如,按ProductName&PriceProductName&排序SKU)。否则,随着文档的添加或更新,结果的顺序将来可能会发生变化。