溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MongoDB 分組統計

發布時間:2020-07-16 10:33:44 來源:網絡 閱讀:355 作者:raqsoft 欄目:大數據

MongoDB 在進行分組統計時如果面對一些比較復雜的計算情況,往往會遇到 shell 腳本過于復雜的問題。而集算器 SPL 語言,則因其有豐富的函數庫及易用性恰好能彌補 Mongo 這方面的不足。

 MongoDB 作為 NoSql 文檔型數據庫,在全球范圍得到廣泛的支持與應用。在比較常用的數據庫功能中,相對于普通的增刪改查,使用 group 聚合分組統計有些復雜,而 MongoDB 也給予了支持。本文將對MongoDb分組的實現方法及示例進行分析,通過在 MongoDB 腳本中操作、使用集算器 SPL 語言操作兩種操作途徑,進行簡單的歸納總結。具體的問題場景包括以下幾個方面:

1. 內嵌數組結構的統計........................................................................... 1
2. 內嵌文檔求和..................................................................................... 2
3. 分段分組結構統計.............................................................................. 4
4. 多字段分組統計................................................................................. 6  

1. 內嵌數組結構的統計

對嵌套數組結構中的數據進行統計處理例如查詢考試科目的平均分及每個學生的總成績:

測試數據:

_idnamesexScroe
1TomF[{"lesson":" Physics","mark":60   },
  {"lesson":" Chemical","mark":72 }]
2JerryM[{"lesson":" Physics","mark":92   },
  {"lesson":" Math","mark":81 }]

期待統計結果:

Physics76
Tom132
Chemical72
Jerry173
Math81


Mongodb腳本:

db.student.aggregate( [
  {$unwind : "$scroe"},
{$group: {
  "_id":   {"lesson":"$scroe.lesson"} ,
  "qty":{"$avg": "$scroe.mark"}
  }
}
] )

db.student.aggregate( [
  {$unwind : "$scroe"},
{$group: {
  "_id": {"name"   :"$name"} ,
  "qty":{"$sum" :   "$scroe.mark"}
  }
}
 ] )

由于各科分數 scroe 是按課目、成績記錄的數組結構,統計前需要將它拆解,將每科成績與學生對應,然后再實現分組計算。這需要熟悉 unwind 與 group 組合的應用。


SPL 腳本 (student.dfx):


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"student.find()").fetch()
3=A2.conj(scroe).groups(lesson:LESSON;avg(mark):AVG)
4=A2.new(name:NAME,scroe.sum(mark):TOTAL)
5>A1.close()

按課目統計的總分數:

LESSONAVG
Chemical72.0
Math81.0
Physics76.0

每個學生的總成績:

NAMETOTAL
Tom132
Jerry173


腳本說明:
       A1:連接 mongodb 數據庫。
       A2:獲取 student 表中的數據。
       A3:將 scroe 數據合并成序表,再按課程分組,計算平均分。
       A4:統計每個學生的成績后返回列名為 NAME、TOTAL 的序表。new 函數表示生成新序表。
       A5:關閉數據庫連接。


       這個嵌套結構統計的例子比較常見,相信很多人都遇到過,需要先拆解再分組計算,主要是熟悉 mongodb 對嵌套數據結構的處理。

2. 內嵌文檔求和

對內嵌文檔中的數據求和處理, 例如統計下面每條記錄中 income,output 的數量和。
測試數據:

_idincomeoutput
1{"cpu":1000, "mem":500,   "mouse":"100"}{"cpu":1000, "mem":600 ,"mouse":"120"}
2{"cpu":2000, "mem":1000,
  "mouse":"50","mainboard":500 }
{"cpu":1500, "mem":300}


期待統計結果:

_id

income

output

1

1600

1720

2

3550

1800

Mongodb腳本:

var fields = [  "income", "output"];
db.computer.aggregate([ 
   { 
      $project:{ 
         "values":{ 
            $filter:{ 
               input:{ 
                    "$objectToArray":"$$ROOT"
               },
               cond:{ 
                  $in:[ 
                     "$$this.k",
                     fields
                  ]
               }
            }
         }
      }
   },
   { 
      $unwind:"$values"
   },
   { 
      $project:{ 
         key:"$values.k",
         values:{ 
            "$sum":{ 
               "$let":{ 
                  "vars":{ 
                     "item":{ 
                          "$objectToArray":"$values.v"
                     }
                  },
                    "in":"$$item.v"
               }
            }
         }
      }
   },
   {$sort: {"_id":-1}},
   { "$group": {
    "_id": "$_id",
    'income':{"$first":   "$values"},
    "output":{"$last":   "$values"}
    }},
]);

filter將income,output 部分信息存放到數組中,用 unwind 拆解成記錄,再累計各項值求和,按 _id 分組合并數據。


SPL腳本:


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"computer.find()").fetch()
3=A2.new(_id:ID,income.array().sum():INCOME,output.array().sum():OUTPUT)
4>A1.close()

統計結果

IDINCOMEOUTPUT
11600.01720.0
23550.01800.0

腳本說明:
      A1:連接數據庫
      A2:獲取 computer 表中的數據
      A3:將 income、output 字段中的數據分別轉換成序列求和,再與 ID 組合生成新序表
      A4:關閉數據庫連接。


      獲取子記錄的字段值,然后求和,相對于 mongo 腳本簡化了不少。這個內嵌文檔與內嵌數組在組織結構上有點類似,不小心容易混淆,因此需要特別注意與上例中的 scroe 數組結構比較,寫出的腳本有所不同。

3. 分段分組結構統計

統計各段內的記錄數量。例如下面按銷售量分段,統計各段內的數據量,數據如下:

_idNAMESTATESALES
1AshleyNew York11000
2RachelMontana9000
3EmilyNew York8800
4MatthewTexas8000
5AlexisIllinois14000


分段方法:0-3000;3000-5000;5000-7500;7500-10000;10000 以上。

期望結果:

Segmentnumber
33
42

Mongo 腳本

var a_count=0;
var b_count=0;
var c_count=0;
var d_count=0;
var e_count=0;
db.sales.find({
}).forEach(
    function(myDoc) {
        if (myDoc.SALES <3000)   {
            a_count += 1;
        }
        else if (myDoc.SALES <5000)   {
            b_count += 1;
        }
        else if (myDoc.SALES   <7500) {
            c_count += 1;
        }
        else if (myDoc.SALES   <10000) {
            d_count += 1;
        }
        else {
            e_count += 1;
        }       
    }
    );
   
print("a_count="+a_count)
print("b_count="+b_count)
print("c_count="+c_count)
print("d_count="+d_count)
print("e_count="+e_count)

這個需求按條件分段分組,mongodb 沒有提供對應的 api,實現起來有點繁瑣,上面的程序是其中實現的一個例子參考,當然也可以寫成其它實現形式。下面看看集算器腳本的實現。

SPL腳本:


AB
1[3000,5000,7500,10000,15000]
2=mongo_open("mongodb://127.0.0.1:27017/raqdb")
3=mongo_shell(A2,"sales.find()").fetch()
4=A3.groups(A1.pseg(int(~.SALES)):Segment;count(1):   number)
5>A2.close()


腳本說明:
       A1:定義 SALES 分組區間。
       A2:連接 mongodb 數據庫。
       A3:獲取 sales 表中的數據。
       A4:根據 SALES 區間分組統計員工數。其中函數 pseg()表示返回成員在序列中的區段序號,int() 表示轉換成整數。
       A5:關閉數據庫連接。


       Mongodb腳本與 SPL 腳本都實現了預期的結果,但函數pseg 的使用讓 SPL 腳本精簡了不少。

4. 多字段分組統計

統計分類項下的總數及各子項數。下面統計按 addr 分類的 book 的數量以及其下不同 book 類型的數量。

addrbook
address1book1
address2book1
address1book5
address3book9
address2book5
address2book1
address1book1
address15book1
address4book3
address5book1
address7book11
address1book1


期望結果:

_idTotalbooksCount
address14book13


book51
address151book11
address23book12


book51
address31book91
address41book31
address51book11
address71book111


Mongo腳本

db.books.aggregate([
    {   "$group": {
          "_id": {
              "addr": "$addr",
              "book": "$book"
        },
          "bookCount": {"$sum": 1}
    }},
    {   "$group": {
          "_id": "$_id.addr",
          "books": {
              "$push": {
                  "book": "$_id.book",
                  "count": "$bookCount"
            },
        },
          "count": {"$sum": "$bookCount"}
    }},
    {"$sort":   { "count": -1} },
    {   "$project": {
          "books": {"$slice": [ "$books", 2] },
          "count": 1
    }}
]).pretty()

先按 addr,book 分組統計 book 數,再按 addr 分組統計 book 數,調整顯示順序。


SPL腳本 (books.dfx):


AB
1=mongo_open("mongodb://127.0.0.1:27017/raqdb")
2=mongo_shell(A1,"books.find()")
3=A2.groups(addr,book;count(book):   Count)
4=A3.groups(addr;sum(Count):Total)
5=A3.join(addr,A4:addr,Total)return A5
6>A1.close()


計算結果:

AddressbookCountTotal
address1book134
address1book514
address15book111
address2book123
address2book513
address3book911
address4book311
address5book111
address7book1111


腳本說明:
        A1:連接 mongodb 數據庫。
        A2:獲取 books 表中的數據。
        A3:按 addr,book 分組統計 book 數顧。
        A4:再按 addr 分組統計 book 數。
        A5:將 A4 中的 Total 按 addr 關聯后合并到序表中。
        B5: 返回序表 A5。
        A6:關閉數據庫連接。


        這個例子中的 SPL 腳本除了一如既往的精簡清晰外,還顯示了如何簡單方便地與 Java 程序集成。

        在 Java 程序中如果要對 MongoDB 實現上面的分組統計功能,需要根據不同的需求重新一五一十地實現,比較麻煩的同時也不通用。而如果用集算器來實現就容易多了,集算器提供了 JDBC 驅動程序,支持在 Java 程序中用 JDBC 存儲過程方式訪問計算結果,調用方法與調用存儲過程相同。(JDBC 具體配置參考《集算器教程》中的“JDBC基本使用”章節)
       Java 調用主要過程如下:
       public void testStudent (){
              Connection con = null;
              com.esproc.jdbc.InternalCStatement st;
       try{
             // 建立連接
             Class.forName("com.esproc.jdbc.InternalDriver");
             con= DriverManager.getConnection("jdbc:esproc:local://");
             //調用存儲過程,其中books是 dfx 的文件名
             st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call books ()");
             //執行存儲過程
             st.execute();
             // 獲取結果集
             ResultSet rs = st.getResultSet();
              。。。。。。。
       catch(Exception e){
             System.out.println(e);
       }

       可以看到,集算器的計算結果能夠很方便地供 Java 應用程序使用。除了上面的調用方式,程序也可以修改成直接加載 SPL 腳本的函數,用 SPL 腳本文件名當參數來實現。同時,集算器也支持 ODBC 驅動,與其它支持 ODBC 的語言集成也與此類似。


       簡單總結一下,MongoDB 的聚合分組計算的操作與存儲文檔的結構息息相關,豐富的文檔結構一方面有利于存儲,同時數據查詢展示也可以做到多樣化,但另一方面也帶來了 shell 腳本操作的復雜性,寫起來比較不容易, 需要考慮的細節、步驟也比較多。通過上面這幾個簡單案例的分析比較,可以看到集算器 SPL 在實現分組統計方面能簡化操作,降低難度,從而有效地幫助我們解決問題。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女