ntile函數

ntile函數能夠對序號進行分組處理,將有序分區中的行分發到指定數目的組中。 各個組有編號,編號從一開始。 對於每個行,ntile 將返回此行所屬的組的編號。這就至關於將查詢出來的記錄集放到指定長度的數組中,每個數組元素存放必定數量的記錄。ntile函數爲每條記錄生成的序號就是這條記錄全部的數組元素的索引(從1開始)。也能夠將每個分配記錄的數組元素稱爲「桶」。ntile函數有一個參數,用來指定桶數。下面的SQL語句使用ntile函數對Order表進行了裝桶處理:算法

select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]

  查詢結果以下圖所示:數組

  使用ntile排名函數-曉菜鳥

  Order表的總記錄數是6條,而上面的Sql語句ntile函數指定的組數是4,那麼Sql Server2005是怎麼來決定每一組應該分多少條記錄呢?這裏咱們就須要瞭解ntile函數的分組依據(約定)。ide

  ntile函數的分組依據(約定):函數

  一、每組的記錄數不能大於它上一組的記錄數,即編號小的桶放的記錄數不能小於編號大的桶。也就是說,第1組中的記錄數只能大於等於第2組及之後各組中的記錄數。ui

  二、全部組中的記錄數要麼都相同,要麼從某一個記錄較少的組(命名爲X)開始後面全部組的記錄數都與該組(X組)的記錄數相同。也就是說,若是有個組,前三組的記錄數都是9,而第四組的記錄數是8,那麼第五組和第六組的記錄數也必須是8。spa

  這裏對約定2進行詳細說明一下,以便於更好的理解。code

  首先系統會去檢查能不能對全部知足條件的記錄進行平均分組,若能則直接平均分配就完成分組了;若不能,則會先分出一個組,這個組分多少條記錄呢?就是 (總記錄數/總組數)+1 條,之因此分配 (總記錄數/總組數)+1 條是由於當不能進行平均分組時,總記錄數%總組數確定是有餘的,又由於分組約定1,因此先分出去的組須要+1條。blog

  分完以後系統會繼續去比較餘下的記錄數和未分配的組數能不能進行平均分配,若能,則平均分配餘下的記錄;若不能,則再分出去一組,這個組的記錄數也是(總記錄數/總組數)+1條。索引

  而後系統繼續去比較餘下的記錄數和未分配的組數能不能進行平均分配,若能,則平均分配餘下的記錄;若仍是不能,則再分配出去一組,繼續比較餘下的......這樣一直進行下去,直至分組完成。ci

  舉個例子,將51條記錄分配成5組,51%5==1不能平均分配,則先分出去一組(51/5)+1=11條記錄,而後比較餘下的 51-11=40 條記錄可否平均分配給未分配的4組,能平均分配,則剩下的4組,每組各40/4=10 條記錄,分配完成,分配結果爲:11,10,10,10,10,曉菜鳥我開始就錯誤的覺得他會分配成 11,11,11,11,7。

  根據上面的兩個約定,能夠得出以下的算法:

 

複製代碼
//mod表示取餘,div表示取整.
if(記錄總數 mod 桶數==0)
{
  recordCount=記錄總數 div 桶數;
  //將每桶的記錄數都設爲recordCount.
}
else
{
  recordCount1=記錄總數 div 桶數+1;
  int n=1;//n表示桶中記錄數爲recordCount1的最大桶數.
  m=recordCount1*n;
  while(((記錄總數-m) mod (桶數- n)) !=0)
  {
    n++;
    m=recordCount1*n;
  }
  recordCount2=(記錄總數-m) div (桶數-n);
  //將前n個桶的記錄數設爲recordCount1.
  //將n+1個至後面全部桶的記錄數設爲recordCount2.
}
複製代碼

 

複製代碼
int recordTotal = 51;//記錄總數.
int tcount = 5;//總組數.
string groupResult = "將" + recordTotal + "條記錄分紅" + tcount + "組,";
int recordCount = 0;//平均分配時每組的記錄數.
//不能平均分配
int recordCount1 = 0;//前n個組每組的記錄數.
int recordCount2 = 0;//第n+1組至後面全部組每一個組的記錄數.
int n = 1;//組中記錄數爲recordCount1的最大組數(前n組).
if (recordTotal % tcount == 0)//能平分.
{
    recordCount = recordTotal / tcount;//每組的記錄數.
}
else//不能平分.
{
    recordCount1 = recordTotal / tcount + 1;//不能平分則先分出一組-前n組每組的記錄數.
    int m = recordCount1 * n;//已分配的記錄數.
    while ((recordTotal - m) % (tcount - n) != 0)//餘下的記錄數和未分配的組不能進行平分.
    {
        //仍是不能平分,繼續分出一組.
        n++;
        m = recordCount1 * n;
    }
    recordCount2 = (recordTotal - m) / (tcount - n);//餘下的記錄數和未分配的組能進行平分或者只剩下最後一組了-第n+1組至後面全部組每一個組的記錄數.
}
//輸出.
if (recordCount != 0)
{
    groupResult += "能平均分配,每組" + recordCount + "個.";
}
else
{
    groupResult += "不能平均分配,前" + n + "組,每組" + recordCount1 + "個,";
    if (n < tcount - 1)
    {
        //groupResult += "第" + (groupNumber + 1) + "組至後面全部組,每組" + recordCount2 + "個.";
        groupResult += "第" + (n + 1) + "組至第" + tcount + "組,每組" + recordCount2 + "個.";
    }
    else
    {
        groupResult += "第" + (n + 1) + "組" + recordCount2 + "個.";
    }
}
ViewData["result"] = groupResult;
複製代碼

  

  根據上面的算法,若是總記錄數爲59,總組數爲5,則 n=4 , recordCount1=12 , recordCount2=11,分組結果爲 :12,12,12,12,11。

  若是總記錄數爲53,總組數爲5,則 n=3 , recordCount1=11 , recordCount2=10,分組結果爲:11,11,11,10,10。

  就拿上面的例子來講,總記錄數爲6,總組數爲4,經過算法獲得 n=2 , recordCount1=2 , recordCount2=1,分組結果爲:2,2,1,1。

 

select ntile,COUNT([ID]) recordCount from 
(
    select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]
) as t
group by t.ntile

 

  運行Sql,分組結果如圖:

  使用ntilt()函數分組-曉菜鳥

  比對算法與Sql Server的分組結果是一致的,說明算法沒錯。:)

相關文章
相關標籤/搜索