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的分组结果是一致的,说明算法没错。:)

相关文章
相关标签/搜索