关于按一定规则进行统计数目的算法(例如按月)--应用程序篇

 承接上篇(关于按一定规则进行统计数目的算法(例如按月)--SQL篇)
http://blog.csdn.net/linaren/archive/2009/11/05/4770228.aspx
*本篇验证语言采用C#
在上篇中查询出来的数据已经知道,但程序里还需要进行处理,才能利用,
查询处理的结构集的结构如下:
____________________________________________________________________
部门名 | 用户名 | 月份 | 对应月份登录的次数 |
──────────────────────────────────
例, 数据如下:
营业部,张三,200901,1
营业部,张三,200902,1
营业部,张三,200903,1
营业部,李四,200902,1
营业部,李四,200903,1
营业部,王二,200901,1
营业部,王二,200902,1

而要求的报表格式如下:
____________________________________________________________________
部门名 | 用户名 | 一月份登录次数 | 二月份登录次数 | 三月份登录次数 |
──────────────────────────────────
按照上面的数据出来的结果如下显示:
营业部,张三,1,1,1
营业部,李四,0,1,1
营业部,王二,1,1,0

■解决方案
㈠根据上面出报表的格式,很容易想到的解决办法之一如下:
创建如报表格式的数据结构(假定为DataTable:rptTable)
然后遍历查询出来的结果集(假定为DataTable:quryTable)
对应每个部门的每个用户在quryTable里查找对应月份的值,
填充到rptTable里去,这个思路实现的代码大致如下:

string []months=new string[]{"200901","200902","200903"};
for(int i=0;i<rptTable.Rows.Count;++i)
{
    // 用户名
    string un = rptTable.Rows[i]["name"].ToString();
    // 部门名
    string bn = rptTable.Rows[i]["dname"].ToString();
    for(int j=0;j<months.length;++j)
    {
        // 实际上如果不是.net的语言,这里还要做个遍历算法来查询数据
        DataRow[] lst = queryTable.Select(
           "name='"+un+"' and dname='"+dn+"' and logindate='"+months[j]+"'");
        if (lst==null){rptTable.Rows[i][months[j]] = 0;}
        else{
            rptTable.Rows[i][months[j]] = Convert.ToInt32(lst[0][months[j]].ToString());
        }
    }
}

这个方案里把rptTable的用户名与部门名填充复杂度忽略掉,
设user数据量为l,devn数据量为m,统计的月数为n
只看填充月份数据的复杂度最优为:l*n*(未知数:用.net提供的select方法,这个数目就相应小得多)
如果非.net语言实现复杂度最坏为:l*n*m
这个代码比较容易解读,但算法上的确存在隐患,当数据量增大时,
效率上折扣就是n倍级下降。
■程序算法优化
从结果集出数据遍历一遍是避免不掉的了,
那么就只有尽量减少按用户与月份遍历结果集的操作,
理想的情况复杂度为l当然是最好的了,
所以就想在遍历查询结果集同时,把对应每个用户的每个月份的统计值填充
按照这个思路优化后的代码如下:
ArrayList monthList = new Arraylist();
monthList.Add("200901");
monthList.Add("200902");
monthList.Add("200903");
ArrayList curLeftMonth = null;
int first = 0;
quryTable.AddColumn("C200901");
quryTable.AddColumn("C200902");
quryTable.AddColumn("C200903");
int first = 0;
string curMonth = "";
string memberNo = "";
int first = 0;
for (int i = 0; i < dt.Rows.Count; ++i)
{
curMonth = quryTable.Rows[i]["logindate"].ToString();
// 发现下一个用户
if (memberNo != (quryTable.Rows[i]["dname"].ToString()+"/n"+quryTable.Rows[i]["name"].ToString()))
{
    // 用户在某个月份没有登录时,统计值设置为0
    if (memberNo != "" && curLeftMonth.Count > 0)
    {
        for (int s0 = 0; s0 < curLeftMonth.Count; s0++)
            quryTable.Rows[first][curLeftMonth[s0].ToString()] = 0;
    }
    curLeftMonth = (System.Collections.ArrayList)monthList.Clone();
    first = i;
    memberNo = quryTable.Rows[i]["dname"].ToString()+"/n"+quryTable.Rows[i]["name"].ToString()
}
// 填充横向对应月份的统计值
monthVal = "C" + quryTable.Rows[i]["logindate"].ToString().Replace("/","");
quryTable.Rows[first][monthVal] = quryTable.Rows[i]["cnum"];
curLeftMonth.Remove(monthVal);
// 删除同一个用户记录,只保留一用户一条记录
if (i != first)
{
    quryTable.Rows[i].Delete();
    quryTable.AcceptChanges();
    i--;
}
}
// 用户在某个月份没有登录时,统计值设置为0
if (memberNo != "" && curLeftMonth.Count > 0)
{
for (int s0 = 0; s0 < curLeftMonth.Count; s0++)
    quryTable.Rows[first][curLeftMonth[s0].ToString()] = 0;
}

// 最后再把多余的列去掉就是报表需要的数据了

quryTable.Columns.Remove("cnum");

quryTable.Columns.Remove("logindate");

这段代码比上段代码虽然执行时间没有达到n*m倍提高,但已经提升了很多倍。
这个算法的关键部分就是这些了,这里我采用的月份是固定的,而实际上往往是

动态的,原理上都是一样的,只不过月份处理前都是动态设置好而已。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值