AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 4859
Accepted
kgriffs
kgriffs
Asked: 2011-08-20 22:07:37 +0800 CST2011-08-20 22:07:37 +0800 CST 2011-08-20 22:07:37 +0800 CST

有没有什么方法可以在 MongoDB 中有效地执行 DENSE_RANK 的等价物?

  • 772

SQL Server 和 Oracle 都有 DENSE_RANK 函数。有没有一种方法可以在 MongoDB 中做类似的事情而不必求助于 MapReduce?换句话说,假设您有一个像这样的 T-SQL select 子句:

SELECT DENSE_RANK() OVER(ORDER BY SomeField DESC) SomeRank

在 MongoDB 中做同样事情的最佳方法是什么?

(注意:这是对MongoDB 问题的重新发布。我希望能从 DBA 那里得到更多反馈……)

sql-server nosql
  • 2 2 个回答
  • 1294 Views

2 个回答

  • Voted
  1. Best Answer
    Richard
    2011-08-21T05:09:37+08:002011-08-21T05:09:37+08:00

    MongoDB 没有任何排名的概念。我能找到的最接近的来自这里:

    这是一些示例数据:

     > db.scoreboard.find()`
     { "_id" : ObjectId("4d99f71450f0ae2165669ea9"), "user" : "dave", "score" : 4 }
     { "_id" : ObjectId("4d99f71b50f0ae2165669eaa"), "user" : "steve", "score" : 5 }`
     { "_id" : ObjectId("4d99f72350f0ae2165669eab"), "user" : "tom", "score" : 3 }
    

    首先,找到用户“dave”的分数:

     db.scoreboard.find({ user : "dave" }, { score : 1 }) { "_id" : ObjectId("4d99f71450f0ae2165669ea9"), "score" : 4 }
    

    然后,统计有多少用户有更高的分数:

     db.scoreboard.find({ score : { $gt : 4 }}).count() 
     1
    

    由于有 1 个更高的分数,因此戴夫的排名为 2(只需将更高分数的计数加 1 即可获得排名)。

    显然,这远非理想。然而,MongoDB 根本没有任何类型的功能,因为它根本不是为这种类型的查询而设计的。

    • 5
  2. kgriffs
    2011-08-25T05:29:41+08:002011-08-25T05:29:41+08:00

    经过一些实验,我发现可以基于 MapReduce 构建排名函数,假设结果集可以容纳最大文档大小。

    例如,假设我有一个这样的集合:

    { player: "joe", points: 1000, foo: 10, bar: 20, bang: "some text" }
    { player: "susan", points: 2000, foo: 10, bar: 20, bang: "some text" }
    { player: "joe", points: 1500, foo: 10, bar: 20, bang: "some text" }
    { player: "ben", points: 500, foo: 10, bar: 20, bang: "some text" }
    ...
    

    我可以像这样粗略地执行 DENSE_RANK:

    var m = function() { 
      ++g_counter; 
    
      if ((this.player == "joe") && (g_scores.length != g_fake_limit)) { 
        g_scores.push({
          player: this.player, 
          points: this.points, 
          foo: this.foo,
          bar: this.bar,
          bang: this.bang,
          rank: g_counter
        });   
      }
    
      if (g_counter == g_final)
      {
        emit(this._id, g_counter);
      }
    }}
    
    
    var r = function (k, v) { }
    var f = function(k, v) { return g_scores; }
    
    var test_mapreduce = function (limit) {
      var total_scores = db.scores.count();
    
      return db.scores.mapReduce(m, r, {
        out: { inline: 1 }, 
        sort: { points: -1 }, 
        finalize: f, 
        limit: total_scores, 
        verbose: true,
        scope: {
          g_counter: 0, 
          g_final: total_scores, 
          g_fake_limit: limit, 
          g_scores:[]
        }
      }).results[0].value;
    }
    

    为了进行比较,这是其他地方提到的“天真”方法:

    var test_naive = function(limit) {
      var cursor = db.scores.find({player: "joe"}).limit(limit).sort({points: -1});
      var scores = [];
    
      cursor.forEach(function(score) {
        score.rank = db.scores.count({points: {"$gt": score.points}}) + 1;
        scores.push(score);
      });
    
      return scores;
    }
    

    我使用以下代码在 MongoDB 1.8.2 的单个实例上对这两种方法进行了基准测试:

    var rand = function(max) {
      return Math.floor(Math.random() * max);
    }
    
    var create_score = function() {
      var names = ["joe", "ben", "susan", "kevin", "lucy"]
      return { player: names[rand(names.length)], points: rand(1000000), foo: 10, bar: 20, bang: "some kind of example text"};
    }
    
    var init_collection = function(total_records) {
      db.scores.drop();
    
      for (var i = 0; i != total_records; ++i) {
        db.scores.insert(create_score());
      }
    
      db.scores.createIndex({points: -1})
    }
    
    
    var benchmark = function(test, count, limit) {
      init_collection(count);
    
      var durations = [];
      for (var i = 0; i != 5; ++i) {
        var start = new Date;
        result = test(limit)
        var stop = new Date;
    
        durations.push(stop - start);
      }
    
      db.scores.drop();
    
      return durations;
    }
    

    虽然 MapReduce 比我预期的要快,但对于更大的集合大小,天真的方法将它从水中吹了出来,尤其是在缓存预热后:

    > benchmark(test_naive, 1000, 50);
    [ 22, 16, 17, 16, 17 ]
    > benchmark(test_mapreduce, 1000, 50);
    [ 16, 15, 14, 11, 14 ]
    > 
    > benchmark(test_naive, 10000, 50);
    [ 56, 16, 17, 16, 17 ]
    > benchmark(test_mapreduce, 10000, 50);
    [ 154, 109, 116, 109, 109 ]
    > 
    > benchmark(test_naive, 100000, 50);
    [ 492, 15, 18, 17, 16 ]
    > benchmark(test_mapreduce, 100000, 50);
    [ 1595, 1071, 1099, 1108, 1070 ]
    > 
    > benchmark(test_naive, 1000000, 50);
    [ 6600, 16, 15, 16, 24 ]
    > benchmark(test_mapreduce, 1000000, 50);
    [ 17405, 10725, 10768, 10779, 11113 ]
    

    所以现在,看起来天真的方法是要走的路,尽管我很想看看今年晚些时候随着 MongoDB 团队继续改进 MapReduce 性能,情况是否会发生变化。

    • 5

相关问题

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

  • NoSQL 和传统的 RDBMS 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    您如何显示在 Oracle 数据库上执行的 SQL?

    • 2 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    我可以查看在 SQL Server 数据库上运行的历史查询吗?

    • 6 个回答
  • Marko Smith

    如何在 PostgreSQL 中使用 currval() 来获取最后插入的 id?

    • 10 个回答
  • Marko Smith

    如何在 Mac OS X 上运行 psql?

    • 11 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Marko Smith

    将数组参数传递给存储过程

    • 12 个回答
  • Martin Hope
    Manuel Leduc PostgreSQL 多列唯一约束和 NULL 值 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler 什么时候应该将主键声明为非聚集的? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    BrunoLM Guid vs INT - 哪个更好作为主键? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick 如何优化大型数据库的 mysqldump? 2011-01-04 13:13:48 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve