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; }
当不使用主键进行遍历计算插入相应的值时所用时间竟然是这么多:
当我使用同样的方法,同样的数据添加主键(即把handleaccyear方法中不使用主键下面的注释去掉后).进行遍历计算等操作,得出的结果竟然有这么大的差别:
补充: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); }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。