为什么这段代码会变慢?

时间:2013-04-05 08:14:24

标签: c# xml database ms-access slowdown

我目前正在将许多Access数据库转换为Xml文件。我以前做过这个,我仍然拥有以前项目的代码。但是,这段代码不会让我按照我的意愿构建xml,这次是我需要做的。我正在使用XDocumentfor - 循环来实现这一点,但是在经过1000行数据后,它会变得非常慢。

阅读XDocument的工作原理告诉我XElement.Add实际上复制了整个xml代码并添加新元素,因为它将所有内容粘贴回文件中。如果这是真的那么这可能就是问题所在。

这是从Access读取数据并将数据写入Xml的部分,看一看是否有任何保存方法。转换具有27列和12 256行的数据库大约需要30分钟,而只有500行的较小版本大约需要5秒。

private void ReadWrite(string file)
{
    using (_Connection = new OleDbConnection(string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Mode=12;Data Source={0}", pathAccess)))
    {
        _Connection.Open();
        //Gives me values from the AccessDB: tableName, columnName, colCount, rowCount and listOfTimeStamps.
        GetValues(pathAccess);

        XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "true"), new XElement(tableName));
        for (int rowInt = 0; rowInt < rowCount; rowInt++)
        {
            XElement item = new XElement("Item", new XAttribute("Time", listOfTimestamps[rowInt].ToString().Replace(" ", "_")));
            doc.Root.Add(item);

            //colCount"-1" prevents the timestamp from beeing written again.
            for (int colInt = 0; colInt < colCount - 1; colInt++)
            {
                using (OleDbCommand cmnd = new OleDbCommand(string.Format("SELECT {0} FROM {1} Where TimeStamp = #{2}#", columnName[colInt] , tableName, listOfTimestamps[rowInt]), _Connection))
                {
                    XElement value = new XElement(columnName[colInt], cmnd.ExecuteScalar().ToString());
                    item.Add(value);
                }
            }
            //Updates progressbar
            backgroundWorker1.ReportProgress(rowInt);
        }
        backgroundWorker1.ReportProgress(0);
        doc.Save(file);
    }
}

这是我旧转换器的代码。此代码几乎不受数据库大小的影响,12 556数据库只需要一秒钟即可进行转换。可能有可能合并这两个吗?

public void ReadWrite2(string file)
{
    DataSet dataSet = new DataSet();
    using (_Connection = new OleDbConnection(string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Mode=12;Data Source={0}", file)))
    {
        _Connection.Open();

        DataTable schemaTable = _Connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });

        foreach (DataRow dataTableRow in schemaTable.Rows)
        {
            string tableName = dataTableRow["Table_Name"].ToString();

            DataTable dataTable = dataSet.Tables.Add(tableName);
            using (OleDbCommand readRows = new OleDbCommand("SELECT * from " + tableName, _Connection))
            {
                OleDbDataAdapter adapter = new OleDbDataAdapter(readRows);
                adapter.Fill(dataTable);
            }
        }
    }
    dataSet.WriteXml(file.Replace(".mdb", ".xml"));
}

编辑:为了澄清,应用程序在执行时会变慢。无论数据库有多大,前500个都需要5秒钟。

更新:好的,所以我现在在周末之后回来了,我在代码中做了一个小调整,通过在一个循环中用值填充锯齿状数组来分离读数和写入并将它们写在另一个。这证明了我的理论错误,实际上是阅读需要花费很多时间。关于如何使用值填充数组而不在循环中访问数据库的任何想法?

UPDATE2:这是切换到DataReader.Read()循环并立即收集所有数据后的最终结果。

public void ReadWrite3(string Save, string Load)
    {
        using (_Connection = new OleDbConnection(string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Mode=12;Data Source={0}", Load)))
        {
            _Connection.Open();
            GetValues(_Connection);

            _Command = new OleDbCommand(String.Format("SELECT {0} FROM {1}", strColumns, tables), _Connection);
            XDocument doc = new XDocument(new XDeclaration("1.0", "utf-8", "true"), new XElement("plmslog", new XAttribute("machineid", root)));
            using (_DataReader = _Command.ExecuteReader())
            {
                for (int rowInt = 0; _DataReader.Read(); rowInt++ )
                {
                    for (int logInt = 0; logInt < colCount; logInt++)
                    {

                        XElement log = new XElement("log");
                        doc.Root.Add(log);

                        elementValues = updateElementValues(rowInt, logInt);

                        for (int valInt = 0; valInt < elements.Length; valInt++)
                        {
                            XElement value = new XElement(elements[valInt], elementValues[valInt]);
                            log.Add(value);
                        }
                    }
                }
            }
            doc.Save(Save);
        }
    }

3 个答案:

答案 0 :(得分:3)

原谅我,但我认为你的生活比你需要的更复杂。如果使用OleDbDataReader对象,则只需打开它并逐行读取Access表,而不必将行数据缓存在数组中(因为您已在DataReader中将其存储)。

例如,我有一些示例数据

dbID    dbName  dbCreated
----    ------  ---------
bar     barDB   2013-04-08 14:19:27
foo     fooDB   2013-04-05 11:23:02

以下代码贯穿表格......

static void Main(string[] args)
{
    OleDbConnection conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Documents and Settings\Administrator\Desktop\Database1.accdb;");
    conn.Open();

    OleDbCommand cmd = new OleDbCommand("SELECT * FROM myTable", conn);
    OleDbDataReader rdr = cmd.ExecuteReader();

    int rowNumber = 0;
    while (rdr.Read())
    {
        rowNumber++;
        Console.WriteLine("Row " + rowNumber.ToString() + ":");
        for (int colIdx = 0; colIdx < rdr.FieldCount; colIdx++)
        {
            string colName = rdr.GetName(colIdx);
            Console.WriteLine("    rdr[\"" + colName + "\"]: " + rdr[colName].ToString());
        }
    }
    rdr.Close();
    conn.Close();

    Console.WriteLine("Done.");
}

...并产生结果......

Row 1:
    rdr["dbID"]: foo
    rdr["dbName"]: fooDB
    rdr["dbCreated"]: 2013-04-05 11:23:02
Row 2:
    rdr["dbID"]: bar
    rdr["dbName"]: barDB
    rdr["dbCreated"]: 2013-04-08 14:19:27
Done.

答案 1 :(得分:2)

您正在从嵌套循环(所有行和列)内部访问数据库

 using (OleDbCommand cmnd = new OleDbCommand(string.Format("SELECT {0} FROM {1} 

最好将数据保存在数组或集合中,然后访问数据库。

答案 2 :(得分:1)

简单的数学计算会告诉你原因。 (数据量)

27 * 12256 = 330912

27 * 500 = 13500

330912/13500 = 24512

所以你的重要声明是24512倍!

(时间紧迫)

30 * 60 = 1800

1800/5 = 360

所以你的时间要大360倍!

你可以看到你的代码似乎没有做坏事。