为啥使用第一个阅读器 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() 运行第二个阅读器比在自己的阅读器上运行它运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gtkmm 有时会自动创建第二个线程?

阅读网页内容(可能是RSS)

为啥 spark.read.parquet() 运行 2 个作业?

Entity Framework Core - 在尝试运行第二个查询时释放

为啥这里使用立即执行?

《如何阅读一本书》读书笔记