为什么在第一个读取器的read()函数中运行第二个读取器比独立运行第二个读取器的read()函数更快?

3

好的,目前我正在运行这段代码,将大量数据从Pervasive数据库移动到Access数据库中。

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()”,代码的运行时间几乎增加了一倍......无论如何,检查数据库,所有数据都在那里。因此,使用常识,我想大概它每次运行第一个读取器时都会执行两组代码,但是所有数据都在那里,为什么呢?销售订单头部分的字段明显少得多,如果它正在为第一个读取器执行读取操作,它不应该在标题表的末尾结束并停止吗?那么为什么所有数据都在那里呢?无论如何,这段代码的运行时间相对较慢,有没有人有改进我的代码的建议?编辑:仅仅为了证明第二个读取器实际上没有返回false:Debugger正如您所看到的,调试器已经进入读取器。

你尝试过在StackExchange网站家族上进行代码审查吗? - GregC
3个回答

4
你确定第二次调用myreader时获得的数据是正确的吗?有些问题不对劲:在循环myreader时应该从第一个SELECT语句中获取数据,但你内部的代码引用了myreader1
所以这里奇怪的事情不是第二次迭代比第一次快:而是第二次迭代返回了你期望的数据。所以问题是:你确定在第二次循环中:
  • 你正在为来自第二个SELECT语句的所有你期望的记录预期的迭代次数中得到期望的迭代次数,例如5000(而不是来自第一个语句的记录数,例如1000)?

  • 你实际上正在获得第二个SELECT语句中每条记录的数据,而不仅仅是每次相同的顶部记录?

关于你提出的问题的第二部分,如何提高数据传输速度,我建议采取以下措施:
  • 通过执行单个INSERT语句添加数据将会很慢。请看这个问题,获取一些非常快的替代方案:
    在.NET/C#中向Access写入大量记录(批量插入)

  • 如果你在Access数据库上做了很多工作,请保持一个永久的连接,而不是打开/关闭它。为什么这会对性能产生很大影响,请参见OLE DB连接池在哪里?
    我通常创建一张名为Dummy的表,其中只有一条记录(无论内容如何),然后在该表上打开一个数据读取器,并将其保持打开状态,直到我关闭应用程序。这可以确保数据库锁定文件保持在原位,不会在每次对数据库执行操作时创建/删除。如果你正在对数据库执行很多操作,你会惊讶于这对性能的影响。


所有数据都已经被传输了,我已经仔细检查过了。我不能保持与数据库的连接开放,因为 A) 它在工作的文件服务器上(这会减慢其他人的速度)B) 其他程序/应用程序使用相同的数据库,如果我保持连接开放,它们也会遇到速度问题。你说批量插入?也许我会试一下 :P - user1200540
保持与Access数据库的链接不会影响其他连接到它的用户。Access是一个文件数据库。如果没有其他连接打开,每次打开/关闭一个连接,ACE引擎都必须检查锁定文件并创建/删除它,这非常耗时。如果锁定文件已经被创建,因为您保持了一个链接打开,那么随后的打开/关闭将非常快。服务器上没有使用任何资源,您可以让100个人连接到虚拟表,这不会以任何方式影响性能。最后关闭虚拟表的人也将删除锁定文件。 - Renaud Bompuis
批量插入/虚拟表让我的程序运行速度快了很多,非常感谢您的帮助。 - user1200540

1

我不知道为什么,但我想我会写出自己问题的答案。

虽然我没有一个好的答案来解释为什么第二个读取器可以成功运行(而不会丢失数据),但我有一些方法可以使这段代码运行得更快,这些方法并未被建议。

首先~

 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替换了它,以防它做了一些我找不到的坏事情。

现在它运行得快多了。我没有像在.NET/C#中将大量记录(批量插入)写入Access建议的那样使用DAO

因为我已经在使用system.data.OleDb了。


1
你能告诉我们哪些行运行得最慢吗?
尝试使用块来处理IDisposable对象,例如DB连接。这样,您在出现异常时将会更加安全。而且您不需要显式调用Close()。
for循环中有很多字符串相加。请尝试使用StringBuilder代替。

如果你在提到try catch块,我之前在这里有一个try catch块,但我把它删除了,因为我认为它会消耗资源,而且它不会抛出任何异常。 - user1200540
数据库访问可能会抛出异常。因为你无法控制SQL引擎代码(我假设)。如果它抛出异常,你将泄漏重要的资源,比如数据库连接。 - GregC
哦...运行最慢的那一行是在Pervasive数据库连接上的.Open操作,我怀疑我无法对此做任何事情 <.< - user1200540
我的帖子中只有一个错别字,这段代码可以成功编译,但根据逻辑它不应该成功!因为“myreader”在第二个读取器上被调用,并且在第一个读取器结束后已经完成了读取。如果我将其拆分成更多的方法,那么在这个论坛上阅读将变得不可能,但真正的问题是,为什么第二个读取器不会抛出某种错误?而且它的速度是使用myreader1的两倍(这似乎是它应该是的)。 - user1200540
@GreC编译器可以看到如果您将读取器放在using块中,则正在执行某些错误操作。但是,仅仅复制粘贴一些代码块而不调整所有引用,我认为这是根本原因。这与编译器检查或逻辑无关。 - Martin
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接