为啥使用第一个阅读器 read() 运行第二个阅读器比在自己的阅读器上运行它运行得更快?
Posted
技术标签:
【中文标题】为啥使用第一个阅读器 read() 运行第二个阅读器比在自己的阅读器上运行它运行得更快?【英文标题】:Why does running a 2nd reader with the first readers read() run faster then running it on it's own readers read?为什么使用第一个阅读器 read() 运行第二个阅读器比在自己的阅读器上运行它运行得更快? 【发布时间】:2012-04-10 02:54:24 【问题描述】:好的,我目前正在运行此代码,以将大量数据从普及数据库移动到访问数据库中
public List<HBPData> LoadData()
loadConnect(); //<-- get's the Pervasive/Access string from a text file
List<HBPData> listofhbpdata1 = new List<HBPData>();
List<HBPData> listofhbpdata2 = new List<HBPData>();
PsqlConnection myConnection = new PsqlConnection();
myConnection.ConnectionString = PervasiveString;
myConnection.Open();
PsqlCommand myCommand = new PsqlCommand("Select NUMBER, CUST_NAME, PO_NO, REQD_DATE, PO_NO, CUST_PO_NO, ORD_DATE, STATUS FROM SALES_ORDER_HEADER WHERE ORD_DATE > 20120220 Order By ORD_DATE desc", myConnection);
PsqlDataReader myreader = null;
myreader = myCommand.ExecuteReader();
while (myreader.Read())
HBPData DataEntity = new HBPData();
DataEntity.NUMBER = (myreader["NUMBER"].ToString());
DataEntity.CUST_NO = (myreader["CUST_NAME"].ToString()).Replace("'","");
DataEntity.PO_NO = (myreader["PO_NO"].ToString());
DataEntity.RequiredDateTime = (myreader["REQD_DATE"].ToString());
DataEntity.Tag = (myreader["PO_NO"].ToString());
DataEntity.Shape = (myreader["CUST_PO_NO"].ToString());
DataEntity.ExpectedCompletion = myreader["ORD_DATE"].ToString().Substring(0, 4) + "/" + myreader["ORD_DATE"].ToString().Substring(4, 2) + "/" + myreader["ORD_DATE"].ToString().Substring(6, 2);
DataEntity.MostRecentStatus = (myreader["STATUS"].ToString());
listofhbpdata1.Add(DataEntity);
PsqlCommand myCommand1 = new PsqlCommand("Select NUMBER, RECNO, CODE, ORDD_DESCRIPTION, BVORDQTY FROM SALES_ORDER_DETAIL WHERE BVRVADDDATE > 20120220 AND (PROD_CODE = \'MET\' OR PROD_CODE = \'MDT\') Order By NUMBER desc", myConnection);
PsqlDataReader myreader1 = null;
myreader1 = myCommand1.ExecuteReader();
while (myreader.Read())
HBPData DataEntity = new HBPData();
DataEntity.NUMBER = (myreader1["NUMBER"].ToString());
DataEntity.RECNO = (myreader1["RECNO"].ToString());
DataEntity.CODE = (myreader1["CODE"].ToString());
DataEntity.DESCRIPTION = (myreader1["ORDD_DESCRIPTION"].ToString());
DataEntity.Quantity = (myreader1["BVORDQTY"].ToString());
listofhbpdata2.Add(DataEntity);
myConnection.Close();
myreader1.Close();
myreader.Close();
System.Data.OleDb.OleDbConnection myAccessConnection = new System.Data.OleDb.OleDbConnection();
myAccessConnection.ConnectionString = AccessString;
myAccessConnection.Open();
System.Data.OleDb.OleDbCommand myAccessCommand3 = new System.Data.OleDb.OleDbCommand("delete from AllOrders", myAccessConnection);
myAccessCommand3.ExecuteNonQuery();
for (int i = 0; i < listofhbpdata2.Count(); ++i)
System.Data.OleDb.OleDbCommand myAccessCommand2 = new System.Data.OleDb.OleDbCommand("" +
"Insert into AllOrders VALUES('" +
listofhbpdata2[i].NUMBER + "'" + ",'" + listofhbpdata2[i].RECNO.ToString() + "'" +
",'" + listofhbpdata2[i].CODE + "','" + listofhbpdata2[i].DESCRIPTION.Replace("\'", "F") + "'" +
",'" + listofhbpdata2[i].Quantity + "')", myAccessConnection);
myAccessCommand2.ExecuteNonQuery();
myAccessConnection.Close();
return listofhbpdata1;
现在,。如果你仔细看,我打错了第二个阅读器,它应该是 while(myreader1.read()) ...我不小心把 myreader.read()
令我惊讶的是 myreader.read() 居然成功运行... 这让我大吃一惊,... 我将其更改为“myreader1.read()” 并且代码的运行时间几乎翻了一番......, 无论如何,检查数据库,所有数据都在那里......
所以,根据常识,我想得很好,它可能只是在每次运行第一个阅读器时执行两组代码,
但是为什么所有的数据都在那里?
Sales_Order_Header 中的字段比 Sales_Order_Detail 少得多,如果它为第一个读取器,它不应该在标题表的末尾完成然后停止吗?那为什么所有的数据都在那里?
不管怎样,这段代码的运行时间比较慢,有没有人对我的代码有什么改进的建议?
编辑:只是为了表明第二个读者实际上并没有返回 false:
如您所见,调试器已进入阅读器
【问题讨论】:
您是否在 StackExchange 系列网站上尝试过代码审查? 【参考方案1】:您确定在第二次调用myreader
时获得了正确的数据吗?
有些事情看起来不对:您循环通过 myreader
应该从您的第一个 SELECT
语句中获取数据,但您的内部代码引用 myreader1
。
所以这里奇怪的不是第二次迭代应该比第一次快:而是第二次迭代返回了你期望的数据。 所以问题是:你确定在第二个循环中:
您将获得第二条 SELECT
语句中所有记录的预期迭代次数,例如 5000(而不是第一条语句中的记录数,例如 1000)。
实际上,您正在获取第二条SELECT
语句中每条记录的数据,而不是每次都获得相同的最高记录。
关于您问题的第二部分,如何提高数据传输速度,我建议如下:
通过执行单个 INSERT
语句添加数据会很慢。
看看这个问题以获得一些非常快速的替代方案:Writing large number of records (bulk insert) to Access in .NET/C#
如果您在 Access 数据库上做了大量工作,请保持对它的永久打开连接,而不是打开/关闭它。关于这会对性能产生重大影响的原因,请参阅Where is the OLE DB connection pooling?。
我通常会创建一个名为Dummy
的表,其中包含一条记录(不管它是什么),然后在该表上打开一个数据读取器,直到我关闭应用程序为止。这确保了每次我对数据库执行某些操作时,数据库锁定文件都保留在原位,并且不会创建/删除。如果您对数据库执行大量操作,您会对这对性能的影响感到惊讶。
【讨论】:
所有数据都在传输中,我已经彻底检查过了,我无法打开与数据库的连接,因为 A)它在工作的文件服务器上(这会减慢其他人的速度) B)其他程序/应用程序使用相同的数据库,如果我让连接保持打开状态,他们会遇到自己的速度问题,并且批量插入你说?也许我会试一试:P 保持对 Access 数据库的链接打开不会影响连接到它的其他用户。 Access 是一个文件数据库。如果没有打开其他连接,则每次打开/关闭一个连接时,ACE 引擎都必须检查锁定文件并创建/删除它,这非常 耗时。如果锁定文件已经创建,因为您保持链接打开,随后的打开/关闭将非常快。没有使用服务器上的资源,您可以有 100 人连接到虚拟表。它不会以任何方式影响性能。最后一个关闭虚拟表的人也将删除锁定文件。 批量插入/虚拟表使我的程序运行得更快了,谢谢你的好先生。【参考方案2】:你能告诉我们运行最慢的线路吗?
尝试使用块来处理 IDisposable 对象,例如 DB 连接。这样,在出现异常时您将是安全的。而且您无需明确调用 Close()。
For 循环有很多字符串添加。请尝试使用 StringBuilder。
【讨论】:
如果你指的是 try catch 块,我之前有一个 try catch 块,我删除了它,因为我认为它会消耗资源,并且它不会抛出任何异常 数据库访问可以并且将会抛出。由于您无法控制 SQL 引擎代码(我假设)。如果它抛出,您将泄漏主要资源,例如数据库连接。 哦.. 运行最慢的行是 .Open 在普遍的数据库连接上,我怀疑我能做些什么 <.> 如果它在从普及数据库读取时抛出,不会发生任何坏事,如果它在写入访问数据库时抛出,当它再次运行时它仍然调用删除?我对这个东西有点新,也许我会重新添加它 @JustinKirk 尝试将您的代码拆分为更多方法。也许范围规则会让你有错字的地方更加明显。 “单一方法,单一职责。”【参考方案3】:我不知道为什么,但我想我会写出我自己问题的答案。
虽然我对为什么第二个阅读器成功运行(不丢失数据)没有很好的答案,但我有一些方法可以让这段代码运行得更快,但没有被建议
首发~
while (myreader.Read())
HBPData DataEntity = new HBPData();
DataEntity.NUMBER = (myreader1["NUMBER"].ToString());
DataEntity.RECNO = (myreader1["RECNO"].ToString());
DataEntity.CODE = (myreader1["CODE"].ToString());
DataEntity.DESCRIPTION = (myreader1["ORDD_DESCRIPTION"].ToString());
DataEntity.Quantity = (myreader1["BVORDQTY"].ToString());
listofhbpdata2.Add(DataEntity);
myConnection.Close();
myreader1.Close();
myreader.Close();
System.Data.OleDb.OleDbConnection myAccessConnection = new System.Data.OleDb.OleDbConnection();
myAccessConnection.ConnectionString = AccessString;
myAccessConnection.Open();
System.Data.OleDb.OleDbCommand myAccessCommand3 = new System.Data.OleDb.OleDbCommand("delete from AllOrders", myAccessConnection);
myAccessCommand3.ExecuteNonQuery();
for (int i = 0; i < listofhbpdata2.Count(); ++i)
System.Data.OleDb.OleDbCommand myAccessCommand2 = new System.Data.OleDb.OleDbCommand("" +
"Insert into AllOrders VALUES('" +
listofhbpdata2[i].NUMBER + "'" + ",'" + listofhbpdata2[i].RECNO.ToString() + "'" +
",'" + listofhbpdata2[i].CODE + "','" + listofhbpdata2[i].DESCRIPTION.Replace("\'", "F") + "'" +
",'" + listofhbpdata2[i].Quantity + "')", myAccessConnection);
myAccessCommand2.ExecuteNonQuery();
这段代码是冗余的有两个原因:
我应该添加到阅读器内的数据库中,而不是创建一个 for 循环,该循环遍历我创建的列表,该列表不用于其他任何操作
我正在清空表并用相同的数据 + 额外数据重新填充它,此时我应该检查我插入的内容是否已经存在,然后只插入当前不存在的行
我已经用这个替换了那个代码:
while (myreader1.Read())
System.Data.OleDb.OleDbCommand myAccessCommand2 = new System.Data.OleDb.OleDbCommand(
"INSERT INTO AllOrders(OrderNumber,RecordNumber,Code, Description, Quantity) " +
"SELECT TOP 1 '" + (myreader1["NUMBER"].ToString()) + "'" + ",'" + myreader1["RECNO"].ToString() + "'" +
",'" + (myreader1["CODE"].ToString()) + "','" + (myreader1["ORDD_DESCRIPTION"].ToString()).Replace("\'", "F") + "'" +
",'" + (myreader1["BVORDQTY"].ToString()) + "'" +
" from AllOrders " +
"WHERE NOT EXISTS(SELECT TOP 1 OrderNumber FROM AllOrders Where OrderNumber = '" + myreader1["NUMBER"].ToString() +"')", myAccessConnection);
myAccessCommand2.ExecuteNonQuery();
现在,
尽管运行 myreader.read 似乎速度更快,但我还是用 myreader1 替换了它,以防万一它做错了我找不到的事情
现在它运行得更快了。我没有像Writing large number of records (bulk insert) to Access in .NET/C# 中建议的那样使用DAO
因为我已经在使用 system.data.OleDb
【讨论】:
以上是关于为啥使用第一个阅读器 read() 运行第二个阅读器比在自己的阅读器上运行它运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 spark.read.parquet() 运行 2 个作业?