欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# DataTable数据遍历优化详解

程序员文章站 2022-03-23 18:46:59
我们在进行开发时,会经常使用datatable来存储和操作数据,我发现在遍历datatable并对数据进行删除和添加操作时速度非常慢,查阅相关资料并测试在添加主键后可以使遍历和操作速度提高很多:测试代...

我们在进行开发时,会经常使用datatable来存储和操作数据,我发现在遍历datatable并对数据进行删除和添加操作时速度非常慢,查阅相关资料并测试在添加主键后可以使遍历和操作速度提高很多:

测试代码,测试的是我们向取出来数据满足flag!=1条件的所有数据的后面添加一条数据(因为这条数据的一些字段值是根据前面的几条满足条件[“accid='” + accid + “' and y='” + year + “' and absid <= ” + absid;]数据的值累加得到的)所以需要进行整个datatable的遍历来计算添加:

public static void test2()
{
 stopwatch watch = new stopwatch();
 using (dbconnection conn = sqlhelper.getconnection("connectionstring"))
 {
  using (sqlcommand cmd = new sqlcommand())
  {
   watch.start();
   cmd.commandtext = string.format(@"
select row_number() over (order by s.accid,s.currid,s.absid,s.flag)as rownum,s.* from test s 
");
   cmd.connection = conn as sqlconnection;
   cmd.commandtimeout = 60000;
   conn.open();
   datatable table = executedatatable(cmd);
   watch.stop();
   console.writeline("从数据库取出数据{0}条", table.rows.count);
   stopwatch watch2 = new stopwatch();
   watch2.start();
   datatable newtable = handleaccyear(table,true);
   watch2.stop();
   console.writeline("数据{0},遍历操作时间:毫秒:{1},秒:{2}", newtable.rows.count, watch2.elapsedmilliseconds, watch2.elapsedmilliseconds / 1000);
  }
  conn.close();
 }
}

填充数据到datatable的方法

public static datatable executedatatable(sqlcommand cmd)
{
  datatable table = new datatable();
  sqldataadapter adaper = new sqldataadapter(cmd);
  adaper.fill(table);
  return table;
}
private static datatable handleaccyear(datatable dt, bool iscurrency)
{
  datatable newdt = dt.clone();
  //不使用主键
  //dt.primarykey = new datacolumn[] {
  // dt.columns["accid"],
  // dt.columns["flag"],
  // dt.columns["absid"],
  // dt.columns["rownum"],
  //};
  if (dt.rows.count > 0)
  {
   object flag = null;
   foreach (datarow row in dt.rows)
   {
    flag = row["flag"];
    if (flag != null && !helper.areequal(flag.tostring(), "1"))
    {
     datarow newrow = newdt.newrow();
     datarow sourcerow = newdt.newrow();
     sourcerow.itemarray = row.itemarray;
     newrow.itemarray = row.itemarray;
     string accid = row["accid"].tostring(),
      year = row["y"].tostring(),
      absid = row["absid"].tostring();
     newrow["flag"] = "5";
     newrow["suminfo"] = "测试数据";
     string filter = "accid='" + accid + "' and y='" + year + "' and absid <= " + absid;
     if (!iscurrency)
     {
      filter = "accid='" + accid + "'and currid='" + row["currid"] + "' and y='" + year + "' and absid <= " + absid;
     }
     datarow[] selectrow = dt.select(filter);
     double debitlc = 0, debitqty = 0, creditlc = 0, creditqty = 0, debitfc = 0, creditfc = 0;
     foreach (datarow item in selectrow)
     {
      debitlc += todouble(item["yeardebitlc"]);
      debitqty += todouble(item["yeardebitqty"]);
      creditlc +=todouble(item["yearcreditlc"]);
      creditqty += todouble(item["yearcreditqty"]);
      if (!iscurrency)
      {
       debitfc += todouble(item["yeardebitfc"]);
       creditfc += todouble(item["yearcreditfc"]);
      }
     }
     newrow["curdebitlc"] = debitlc;
     newrow["curdebitqty"] = debitqty;
     newrow["curcreditlc"] = creditlc;
     newrow["curcreditqty"] = creditqty;
     //newrow["curdebitlc"] = dt.compute("sum(yeardebitlc)", filter);
     //newrow["curdebitqty"] = dt.compute("sum(yeardebitqty)", filter);
     //newrow["curcreditlc"] = dt.compute("sum(yearcreditlc)", filter);
     //newrow["curcreditqty"] = dt.compute("sum(yearcreditqty)", filter);
     if (!iscurrency)
     {
      //newrow["curcreditfc"] = dt.compute("sum(yearcreditfc)", filter);
      //newrow["curdebitfc"] = dt.compute("sum(yeardebitfc)", filter);
      newrow["curcreditfc"] = creditfc;
      newrow["curdebitfc"] = debitfc;
     }
     newdt.rows.add(sourcerow);
     newdt.rows.add(newrow);
    }
    else
    {
     datarow sourcerow = newdt.newrow();
     sourcerow.itemarray = row.itemarray;
     newdt.rows.add(sourcerow);
    }
   }
  }
  return newdt;
 }

当不使用主键进行遍历计算插入相应的值时所用时间竟然是这么多:

C# DataTable数据遍历优化详解

当我使用同样的方法,同样的数据添加主键(即把handleaccyear方法中不使用主键下面的注释去掉后).进行遍历计算等操作,得出的结果竟然有这么大的差别:

C# DataTable数据遍历优化详解

补充:c# datatable数据量大,循环处理数据的时候优化速度

相信大家用for循环datatable数据的不会太少,这个在数据量比较小的时候可以接受,但是数据量大的时候却会造成cpu占用过高,甚至把电脑资源耗尽卡死至无限等待,

其实一些循环耗时的操作可以用线程池分块来处理,这样会减轻cpu很多压力,好比食堂打饭,当只有一个窗口的时候势必等待的时间会非常的长,但是多开几个窗口的时候却大大提高效率,

c#中用线程池就可以做到,本来一开始的时候我用的是为每个区块开一个线程,但是有一个问题就是开了那么多的线程没办法结束他们,后来我想到了线程池,

具体代码如下:

int sid = dt.rows.count % 100 == 0 ? (dt.rows.count / 100) : (dt.rows.count / 100 + 1);
    for (int a = 1; a <= sid; a++)
    {
     object aa=a.tostring() + "," + sid.tostring();
     threadpool.queueuserworkitem(todo
      , aa);
    }
 public void todo(object aa)
 {
  string sql = "";
  int startindex = convert.toint32(aa.tostring().split(',')[0]);
  int limitstep = convert.toint32(aa.tostring().split(',')[1]);
  for (int i = (startindex > 1 ? ((startindex - 1) * 100) : 0); i < (startindex == limitstep ? (dt.rows.count) : startindex*100); i++)
  {
   //todo数据操作
  }
  thread.sleep(2000);
 }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。