最近饶有兴致的修改了一个小程序,结果出乎以外。
程序业务逻辑比较简单,把这个文本文件中数据更新到数据库中,中间还包含了一些字符串解析、数据计算等过程。这个文本文件大小为200多M,记录条数为70多万条。
原来的程序是用c++/MFC开发的,数据库连接采用odbc方式。
伪代码大致如下:
先SQLPrepare创建更新语句,SQLBindParameter绑定参数,SQLExecute执行语句。
这样处理下来一般10分钟左右可以处理完成。现在推出.net 2.0,在VS.net 2005用C#重新编写了以上小程序。
界面是弄的华丽了。但是效率老牛一样,1000条都没更新完成已经耗去我1分多钟。
我去掉数据库更新部分代码,对于文件及字符串处理,效率还可以,为什么以下代码效率会如此低下?
现在是每条记录都调用如下函数,sql语句根据参数生成。 SqlCommand command = null; try
{
command = conn.CreateCommand();
command.CommandText = "...";
command.CommandType = CommandType.Text; return command.ExecuteNonQuery();
}
catch (SqlException e)
{
//...
}
是否有啥好方法能够提高执行效率?
程序业务逻辑比较简单,把这个文本文件中数据更新到数据库中,中间还包含了一些字符串解析、数据计算等过程。这个文本文件大小为200多M,记录条数为70多万条。
原来的程序是用c++/MFC开发的,数据库连接采用odbc方式。
伪代码大致如下:
先SQLPrepare创建更新语句,SQLBindParameter绑定参数,SQLExecute执行语句。
这样处理下来一般10分钟左右可以处理完成。现在推出.net 2.0,在VS.net 2005用C#重新编写了以上小程序。
界面是弄的华丽了。但是效率老牛一样,1000条都没更新完成已经耗去我1分多钟。
我去掉数据库更新部分代码,对于文件及字符串处理,效率还可以,为什么以下代码效率会如此低下?
现在是每条记录都调用如下函数,sql语句根据参数生成。 SqlCommand command = null; try
{
command = conn.CreateCommand();
command.CommandText = "...";
command.CommandType = CommandType.Text; return command.ExecuteNonQuery();
}
catch (SqlException e)
{
//...
}
是否有啥好方法能够提高执行效率?
解决方案 »
- 强类型DataSet,真的很抓狂
- 发布一个图像处理小工具:Crimm Imageshop 2.0
- C# 通过checkbox选中,来控制BUTTON可用。错在哪里?
- 去哪找managed directx 的c#示例代码,下了两个版本(2009,2007)的directx都是c++的示例
- 数值累加失败...
- 帮我看看,这个多线程的问题。是不是我基础太差!!!!!!!!!!!
- C# .net web程序,如何监听客户端浏览器打印状态
- 循环读dataset
- c#DataGridView CellEndEdit方法中怎样获取键盘输入
- 请教:什么是泛型技术?
- 在OWC中如何显示数值?
- TreeView 树控件中对某个节点当选中时旁边出现一个按钮,点击删除对应该节点的数据库内容
ado.net:1000条,1分钟多我虽没做过类似的测试,但觉得不会慢得这么多。
我也很诧异。我之前的方法是只创建一次preparestatement,然后根据参数变化,执行execute语句。
而在ado。net中我没有找到PrepareStatement,所以每更新一条记录都去创建command,
是不是这个有关系?
而在ado。net中我没有找到PrepareStatement,所以每更新一条记录都去创建command,
是不是这个有关系?哦,你每次都是新建command,为什么不用同一个command呢,只是修改command参数的值呢,然后去执行呢
{
sqlConn.Open();//Connect to sql server
}
catch( Exception err )
{
MessageBox.Show( err.Message );
return;
}SqlCommand myCommand = new SqlCommand( "UpdateEmployee" );
myCommand.Connection = sqlConn;
myCommand.CommandType = CommandType.StoredProcedure;
//Set parameters
myCommand.Parameters.Add( "@EmployeeName",
SqlDbType.VarChar, 20 );
myCommand.Parameters.Add( "@Password",
SqlDbType.VarChar, 20 );
myCommand.Parameters.Add( "@Description",
SqlDbType.VarChar, 255 );for( int i = 0; i < 5; i++ )
{
// Update parameters value
myCommand.Parameters["@EmployeeName"].Value = DateTime.Now.Ticks.ToString();
myCommand.Parameters["@Password"].Value = DateTime.Now.Ticks.ToString();
myCommand.Parameters["@Description"].Value = DateTime.Now.Ticks.ToString();
myCommand.ExecuteNonQuery();
System.Threading.Thread.Sleep( 5 );
}// Release resources
myCommand.Dispose();
sqlConn.Close();
然后再用SqlDataAdapter把这个DataTable给更新到数据库中去。想要效率高就按上面那样做。
每一条记录都去创建一个SqlCommand对象实在是太恐怖了,我怀疑主要的开销会是内存分配和垃圾回收……
然后再用SqlDataAdapter把这个DataTable给更新到数据库中去。我倒不赞同,70m的文件,都解析到DataTable,那得多大。
楼主系统慢的主要原因在打开文件时可能是一次把200都读出来;时间消耗在内存申请上了;一次最大读100条数据;
把你的代码改为
SqlCommand command = null; try
{
command = conn.CreateCommand();
command.CommandText = "insert into ..... insert into ";//xia写一百个insert into 语句。
command.CommandType = CommandType.Text; return command.ExecuteNonQuery();
}
catch (SqlException e)
{
//...
}
不服的可以根我的方法PK一下。
且不说一百条拼接的Insert语句是否正确与否,光是拼接一百条Insert语句就够累得,还有你是否考虑到CommandText的长度限制,以及SQL语句的字符限制等等。你这样的代码可读性差,调试和维护都很麻烦。看了你的回复,我不禁想问一句,你是程序员吗?
如果经常要把一个文本文件导入到数据库,还是推荐Parse成DataTable或者Parse成SqlCommand来做。如果因为这样而导致速度慢,则解决方案还有很多,例如开后台线程分批慢慢处理。
如果只是要弄一次两次,根本就用不着写程序,直接用SqlServer的导入导出程序就成。直接Parse成SqlText的确是可以达到很高的效率,但再快也快不过直接导入。
我同意渔翁的做法,多条记录作数据操作,一定要避免重复创建操作对象,包括数据连接的打开关闭,都尽量一次完成,否则效率自然会底下.
渔翁的方式效率是比较高的.to 我一直在寻找() ( ) 信誉:96 :
你的代码太业余了,自己多练练再出来讨论吧,还说你的效率最高??呵呵
的确没有试验过;用的是感觉;我们可以测试测试。
你看过ADO的底层吗?没有,你一定没有;你知道ADO是怎么实现的吗。你不知道。
我告诉你,我看过,我反编译后看的;ADO给你提供的所有类最后都会生成SQL语句;ADO发送给服务器的也是SQL语句(我用网卡侦听器侦听过);你通过使用ADO提供的类来操作;不就是多一层操作。
再告诉你:Knight94(愚翁);我上CSDN的时候你还没毕业呢;我用C、C++操作ODBC底层API的时候你可能还不知道什么叫ADO呢;别看你3颗星,就用这种口气跟我说话,我其实不想跟你比什么,我告诉你我现在的确不是程序员了,我是一个软件公司的经理公司不大,每年利润差不多90万。我以前是在社区中“长大”的,我必须多逛逛社区,好好帮助我们的弟弟妹妹,可能太忙,回答的问题有限,所有星数比你低。Knight94(愚翁)别这样,别以为自己特L B要知道山外有山,我感觉我自己就挺一般的。只是机遇好点。Knight94(愚翁)多多看看底层的东西吧,对你一定有好处的,别以为自己会用.Net的类就是高手;牛人。
不好意思,说得多了点。
兄弟,你说我的方法太业余;是不是只用.Net类才教专业呢。搞笑,是不是用汇编的、C、C++、那就更加业余了。自己不会写SQL语句,不会底层的操作就说别人业余,悲哀、真的特悲哀。
看了你的发言大吃一惊,也许你底层东西操作比较多罢了,也没什么特别,你自己用汇编写个类ODBC出来用,我想完全没有必要。并且从你的语句中感觉你却实只是在小公司呆过,如果你的用户同时有几千台机在用你的C/S程序,保证你会觉得可维护性,稳定性远远大于效率。用ado和ODBC相比也不可能相差很大,否则微软的东西还有人用,他们的程序员当然也不是吃白饭的?
SqlConnection connection = New SqlConnection(connectionString);
string queryString = "BULK INSERT Northwind.dbo.[Order Details] " +
"FROM 'f:\mydata\data.tbl' " +
"WITH ( FORMATFILE='f:\mydata\data.fmt' )";
connection.Open();
SqlCommand command = new SqlCommand(queryString, connection);command.ExecuteNonQuery();
connection.Close();
还有NET2.0里已经有个SqlBulkCopy类,专用于批量复制的
重新造轮子有意思吗?....
有本事微软咋不叫你去写ADO.NET??...
大家都是来学习的.....以和为贵
....
保证你会觉得可维护性,稳定性远远大于效率
....
用ado和ODBC相比也不可能相差很大,否则微软的东西还有人用,他们的程序员当然也不是吃白饭的".感觉这位仁兄没怎么接触过 OLTP 系统, 一棍子把人打死了. 在一个大型 OLTP 中没有效率搞不好你的程序刚运行 3 秒钟就服务器当机了, 这时候你还谈什么可维护性和稳定性 ? 电信的计费系统怎么做出来的 ? 难道是用 ado 做出来的 ? google 的核心服务器用的不是 ibm 也不是 hp 而是自己开发的. 难道 ibm, hp 是垃圾, 他们的工程师都是吃白饭的 ? 又或者 google 是小公司 ?另对阁下的如下高论: "ado 和 odbc 的差别不可能相当大". 本人敬仰之情犹如黄河之水 .....
有本事微软咋不叫你去写ADO.NET??..."
这不是有没有意思的问题;我想对你说我这段时间重写了WebService的实现,在你看来可能也是“重新造轮子”;我想对你说我重写WebService的的原因是因为他有许多地方我使用不方便;比如不支持事件;WebService在客户端调用不是特别灵活。。
有时候“重新造轮子”是有必要的,SUN27战斗机那么好用为什么我们还要造歼10,因为如果不造歼10我们就造不出来歼14。
首先对我那句粗鲁的回复道歉,说"业余"这话就有些太幼稚了.就事论事,我们来看渔翁的代码,我不知道哪里用到了DataTable,为什么把DataTable牵扯到里面来了?
关于大量数据的操作,我不是行家,但是也是作过一些的,参数化查询无疑是一个好方法,而拼接长sql,性能未必就比循环执行要快.而且在项目里面灵活性,扩展,维护难度都是个问题.
另外在mysql里面试试长sql,会死人的.DataTable似乎是lvony提出来的,用这个,我也是很不赞同,所以,上面的争论似乎完全是场误会.大家的目的都是探讨问题,虽然真理需要辩论,但是争论过于激烈也不好了
下面的这三句,
command = conn.CreateCommand();
command.CommandText = "...";
command.CommandType = CommandType.Text;command = conn.CreateCommand();
command.CommandType = CommandType.Text;
这两句只要写一次就够了.不用每条记录作一次,这相当于重复作了70W次重复操作.
还有就是参数化查询,效率明显高于直接执行SQl,不信的可以测试一下.
" 重新造轮子有意思吗?....
有本事微软咋不叫你去写ADO.NET??..."
这不是有没有意思的问题;我想对你说我这段时间重写了WebService的实现,在你看来可能也是“重新造轮子”;我想对你说我重写WebService的的原因是因为他有许多地方我使用不方便;比如不支持事件;WebService在客户端调用不是特别灵活。。
有时候“重新造轮子”是有必要的,SUN27战斗机那么好用为什么我们还要造歼10,因为如果不造歼10我们就造不出来歼14。
嗯,如果真是这样,那我真的太佩服你了..重写webservice..高人
可维护性绝对至上这个也是Java和.NET兴起之后的一种理论,可是.NET还是保留了Goto.可以试试BULK INSERT
我们比的是方法,用什么语音都无所谓VB.Net、C#、j#、C++(托管代码),最好别用IL,毕竟这中方法在项目中不太实用。
我先开一个目录贴,什么叫目录贴呢,就是在本贴中只讨论要操作的东西,然后再对每一个具体要讨论的内容开一个帖子。
希望大家给个面子,多多灌水。
请大家先去:http://community.csdn.net/Expert/topic/4730/4730832.xml?temp=.8603479
我等着大家。
使用SqlDataAdapter来管理数据的更新访问。
另外,可以修改成存储过程代替sql 字符串。
个人对Knight94(愚翁) 的提法不敢苟同. 如果是我,咱先不说代码如何,从实现方法上我是会用我一直在寻找() 的那种方式.
这是给铁通做的一个导出数据到Execel的其中一段代码
导出300M的文件用时45分钟左右,不知算快还是慢
在这里我就用了大量的insert sql 语句
这些insert sql 语句我先用arrInsert 数组存起来
private void CreateRecord()
{
string onnection "Provider=Microsoft.Jet.OleDb.4.0;Data Source="+strFilePath2Name;
OleDbConnection conn = new OleDbConnection(connection);
conn.Open();
OleDbCommand cmd = new OleDbCommand();
foreach(string insert in arrInsert)
{
cmd = new OleDbCommand(insert,conn);
cmd.ExecuteNonQuery(); }
conn.Close();
conn =null;
}
--------------------不見得 當並發量太大時首先要考慮的是性能 跑都跑不動你還奢談什麽穩定、可維護(除非你要的是一動不動的“穩定”和因此完全不需要維護的結果) QQ的並發數大不大?人家就用原生代碼(看他招的人就知道),按照你的理論是十惡不赦了。
给段代码大家看看
这是给铁通做的一个导出数据到Execel的其中一段代码
导出300M的文件用时45分钟左右,不知算快还是慢
在这里我就用了大量的insert sql 语句
这些insert sql 语句我先用arrInsert 数组存起来这段代码肯定慢了,DBCommand对象没有必要每次循环创建一次
性能的问题一般是多方面的
但是这个问题明显主要是对象反复创建导致,再一个就是数据库的连接要保持打开直到所有的数据都提交完,典型的例子就是微软建议用StringBuilder在频繁造作的字符串中代替string,就是避免对象反复创建。
我一直在寻找 说得方法我觉着没必要。渔翁的做法比较好。
再就是 我一直在寻找 动不动就地层,但是水平确实不怎么样。
我想楼主最好不要每次执行都这样做,把它们单独出来,只create一次,每次再赋值给参数,再excute,这样效率会高一点吧,
1、1000条/分和7w+条/分是只纯粹的数据库操作消耗时间的比较还是整个程序消耗时间的比较?楼主能确定程序的瓶颈一定是在贴出的这段代码里面吗?这个结论是出自直觉还是用某种的分析方法和工具得出呢?2、就这段代码而言,在循环内部使用conn.CreateCommand()对执行效率会有影响。下面是我的3段代码,供参考。第一段,在循环外初始化SqlCommand:
// 假设insert语句有2个参数,第一个是整数,第二个是长度为100的nvarchar
const string Param1 = "@p1";
const string Param2 = "@p2";string insertStatement = GetInsertStatement (Param1, Param2);
System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand (insertStatement, conn);command.Parameters.Add(Param1, SqlDbType.Int);
command.Parameters.Add(Param2, SqlDbType.NVarChar, 100);
command.Prepare ();第二段,循环体:
// 导入记录
command.Parameters[Param1] = value1;
command.Parameters[Param2] = value2;
command.ExecuteNonQuery();第三段,第一段代码中用到的GetInsertStatement方法:
private string GetInsertStatement(string param1, string param2)
{
return string.Format("Insert into table (f1, f2) values ({0}, {1})", param1, param2);
}
导出文本文件250万条左右15-20秒左右,没有尝试过一次导出更多的,不过我觉得也不会太慢。经常操作的数据量在50--300万也从来没有超过一分钟的。
光这个写100个语句我就不赞同,把人已经给累死了
管别人编码风格什么事......
to: kinthtime(中关一杰)
你的代码为什么不这样写呢?
private void CreateRecord()
{
string onnection "Provider=Microsoft.Jet.OleDb.4.0;Data Source="+strFilePath2Name;
OleDbConnection conn = new OleDbConnection(connection);
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
foreach(string insert in arrInsert)
{
cmd.CommendText = insert;
cmd.ExecuteNonQuery(); }
conn.Close();
conn =null;
}
个人觉得这样会好一些吧?谢谢赐教!
我对使用Command的两种方式(一次创建,循环使用 & 每次创建和使用)进行了Insert操作对比测试,发现完全是一样,没有任何明显区别,所以问题不应该是创建Command对象这里,大家可以省略对象创建的时间了.2):Command更新时间
另外LZ的时间确实奇怪,在空表的情况下,我Insert 1000条数据只要6s,Insert 10000条只要1分钟,在表中有15万条记录的情况下,Insert 1000 条和10000条的时间和空表插入时间几乎还是一样的.3):Question
问题是不是出在数据库和表结构上?LZ不妨把你的数据库,表结构和更新语句发出来,让大家了解的更清楚一些,这样也许大家可以帮你找到问题所在.PS:我测试的机器是普通PC(xp+vs2003),数据库服务器双CPU+1G Memory+win2000+Sql Server2000,随便建了一个测试表
create table InsertTest
(filed1 nvarchar(30) null,field2 nvarchar(30) null,field3 nvarchar(100) null)
偶像!如果是1万条记录,是不是要写10000条sql语句???????????恐怖。
command.CommandText = "insert into ..... insert into ";//xia写一百个insert into 语句。
偶像!如果是10万条记录,是不是要写100000条sql语句???????????恐怖。
1。从文本取数据再多也不慢(一次全部取入),应该说比想象中快得多;
2。就是把取出的数据放到DataTable中,这个耗内存,但速度不并慢。
3。当最后生成Insert的语句,往SQLSERVER数据库中执行,非常慢。
但要记住 MS的东西提倡的是方便
效率向来就不怎么地
而且内存也特能吃
所以听听别的意见还是有好处的
1、一次执行大批量sql语句的情况下,参数化的sql语句效率比每次一个sql语句高得多,有兴趣的各位可以参考一下数据库的资料,这个和上层程序无关的。(注,对于非参数化sql,即使'select 1'和'select 2'都会被数据库判定为完全不同的sql语句而重新解析 )2、建议使用存储过程3、ODBC的优点在于使用简单和标准接口,而不在于执行效率...
Oracle: 外部表我都用过,速度很快
偶像!如果是1万???,是不是要?10000?sql?句???????????恐怖。-----------------------------------
你誤解人家意思了!insert into語句是用循環生成,不是一條一條手動寫進去,這不可能!
CREATE PROCEDURE pt_BooksInsertBorrowList
@strXMLList text --xml串
AS
declare @hdoc int
exec sp_xml_preparedocument @hdoc output,@strXMLListinsert into BooksBorrowList (BookBorrowID, BookID, BookBorrowCardID, BookBorrowUserCode, BookBorrowUserName, BookBorrowDate, BookBorrowPlanReturnDate,
BookManageUserCode, BookManageUserName, BookBorrowIsKeepOn, BookBorrowFatherID, BookBorrowRe, BookReturnRe)
select BookBorrowID, BookID, BookBorrowCardID, BookBorrowUserCode, BookBorrowUserName, BookBorrowDate, BookBorrowPlanReturnDate,
BookManageUserCode, BookManageUserName, BookBorrowIsKeepOn, BookBorrowFatherID, BookBorrowRe, BookReturnRe
from openxml(@hdoc,'/root/record',2 )
with (BookBorrowID int,BookID varchar(50),BookBorrowCardID varchar(50),BookBorrowUserCode varchar(50),BookBorrowUserName varchar(50),BookBorrowDate datetime,
BookBorrowPlanReturnDate datetime,BookManageUserCode varchar(50),BookManageUserName varchar(50),BookBorrowIsKeepOn int,BookBorrowFatherID int,BookBorrowRe varchar(500),BookReturnRe varchar(500))
{
command = conn.CreateCommand();
command.CommandText = @"Insert Into TBName(F1,F2,F3...)Values(@F1,@F2,@F3...)";
SqlParameter p1=command.Parameters.Add("@F1",DbType.NChar,10);
SqlParameter p2=command.Parameters.Add("@F2",DbType.NChar,10);
SqlParameter p3=command.Parameters.Add("@F3",DbType.NChar,10);
command.CommandType = CommandType.Text;
While(eof()) //假设的循环
{
p1.Value=v1; //赋值
p2.Value=v2; //赋值
p3.Value=v3; //赋值
command.ExecuteNonQuery();
}
}
catch (SqlException e)
{
//...
}//这样肯定快
ADO.NET 比以前的ADO 效率要高,前提是你必需正确的使用它。楼主一定是组合了一个很大的SQL 串,如此大的串传递给SQL SERVER本身就需要很大的资源,和时间。
将SQL串适当的减小,并且使用参数,使SQL串保持不变。SQL SERVER就会很高重复效率执行,这样比执行一个大字符串效率高。
1、
SqlCommand command = null;try
{
command = conn.CreateCommand();
command.CommandType = CommandType.Text;
//用循环语句拼接100条。
command.CommandText = "insert into ..... insert into ";
//如果拼接语句时有参数,再用循环语句给command添加参数 return command.ExecuteNonQuery();
}
catch (SqlException e)
{
//...
}
======================
2、SqlCommand command = conn.CreateCommand();
command.CommandType = CommandType.Text;
try
{
//用循环语句拼接100条。
for(int i = 0; i < 100; i++)
{
command.CommandText = "insert into ..... ";
//如果拼接语句时有参数,再用循环语句给command添加参数
command.ExecuteNonQuery();
}
}
catch (SqlException e)
{
//...
}那种方法更快?
关于IConnection和IDataCommand对象的创建之前我做过测试的,重复创建对象和重复打开关闭连接都会明显影响性能.当时测试的好象是oledb的
insert语句执行的时候,随着数据量增多,会越来越慢,除非你的表里面没有任何主键和索引.你可以重新测试一下.