批处理数据:Visual Studio

本文关键字:Studio Visual 数据 批处理 | 更新日期: 2023-09-27 18:16:47

我正在学习c#,我正在从一个数据库中读取一个巨大的表并将其加载到我的DataTable

由于表包含一组巨大的行(180万+),我一直得到一个内存不足的错误,我试图打破它,并复制100,000行,一次清理内存和重做,直到表中的所有数据从源被加载到我的DataTable

你能不能看看我的代码然后告诉我我是否在正确的轨道上?在我看来,我一遍又一遍地读取前10万行,我的程序无限期地运行。

是否有一个计数器我需要添加到我的DataTable ?所以它添加下一组行??我的代码片段如下:

    public IoSqlReply GetResultSet(String directoryName, String userId, String password, String sql)
    {
        IoSqlReply ioSqlReply = new IoSqlReply();
        DataTable dtResultSet = new DataTable();
        IoMsSQL ioMsSQL = null;
        int chunkSize = 100000;
        try
        {
            using (OdbcConnection conn = new OdbcConnection(cs))
            {
                conn.Open();
                using (OdbcCommand cmd = new OdbcCommand(sql, conn))
                {
                    using (OdbcDataReader reader = cmd.ExecuteReader())
                    {

                        for (int col = 0; col < reader.FieldCount; col++)
                        {
                            String colName = reader.GetName(col);
                            String colDataType = reader.GetFieldType(col).ToString(); ;
                            dtResultSet.Columns.Add(reader.GetName(col), reader.GetFieldType(col));
                        }
                                                 // now copy each row/column to the datatable
                        while (reader.Read())       // loop round all rows in the source table
                        {
                            DataRow row = dtResultSet.NewRow();
                            for (int ixCol = 0; ixCol < reader.FieldCount; ixCol++)     // loop round all columns in each row
                            {
                                row[ixCol] = reader.GetValue(ixCol);
                            }

                            // -------------------------------------------------------------
                            // finished processing the row, add it to the datatable
                            // -------------------------------------------------------------
                            dtResultSet.Rows.Add(row);
                                GC.Collect();       // free up memory
                        }//closing while
                        ioSqlReply.DtResultSet = dtResultSet;       // return the data table
                        ioSqlReply.RowCount = dtResultSet.Rows.Count;
                        Console.WriteLine("DTRESULTSET:ROW COUNT FINAL : " + dtResultSet.Rows.Count);
                        ioSqlReply.Rc = 0;
                    }
                }
            }
        }

批处理数据:Visual Studio

您应该限制Sql中的行数,例如…

SELECT TOP 10000 * FROM SomeTable;

如果你不这样做,并且你的查询有1.8M,那么没有系统能够处理它。

但是这将使你的应用程序只处理前10000行…如果你需要处理所有的行,那么你应该迭代SQL的执行,直到没有更多的行…例如

public IoSqlReply GetResultSet(String directoryName, String userId, String password, String sql)
{
    IoSqlReply ioSqlReply = new IoSqlReply();
    DataTable dtResultSet = new DataTable();
    IoMsSQL ioMsSQL = null;
    bool keepProcessing = true;
    try
    {
        using (OdbcConnection conn = new OdbcConnection(cs))
        {
            conn.Open();
            while (keepProcessing)
            {
               using (OdbcCommand cmd = new OdbcCommand(sql, conn))
               {
                   using (OdbcDataReader reader = cmd.ExecuteReader())
                   {
                      if (reader.HasRows)
                      {
                        for (int col = 0; col < reader.FieldCount; col++)
                        {
                           String colName = reader.GetName(col);
                           String colDataType = reader.GetFieldType(col).ToString(); ;
                           dtResultSet.Columns.Add(reader.GetName(col),     reader.GetFieldType(col));
                        }
                        // now copy each row/column to the datatable
                        while (reader.Read())       // loop round all rows in the source table
                        {
                            DataRow row = dtResultSet.NewRow();
                            for (int ixCol = 0; ixCol < reader.FieldCount; ixCol++)     // loop round all columns in each row
                            {
                                row[ixCol] = reader.GetValue(ixCol);
                            }

                           // -------------------------------------------------------------
                           // finished processing the row, add it to the datatable
                           // -------------------------------------------------------------
                            dtResultSet.Rows.Add(row);
                            GC.Collect();       // free up memory
                        }//closing while
                        ioSqlReply.DtResultSet = dtResultSet;       // return the data table
                        ioSqlReply.RowCount = dtResultSet.Rows.Count;
                        Console.WriteLine("DTRESULTSET:ROW COUNT FINAL : " + dtResultSet.Rows.Count);
                        ioSqlReply.Rc = 0;
                      }
                      else
                      {
                        keepProcessing = false;
                      }
                   }
                }
            }
        }
    }

这是一个非常粗略的例子…它可以改进,但我认为这是一个简单的解决你的问题。

1)您在64位机器上运行吗?

2) 1,800,000行。假设平均每行1KB。1.8GB内存

3)是否有一个原因,你必须加载在内存中的一切?您能将数据流输入并一次一行地处理它吗?

4)为什么不让数据库来处理大的表而不是你的客户端程序呢?

如果你正在处理大型数据表,你可能不得不采取不同的方法,而不是仅仅将所有内容加载到内存中。你需要一个新的设计。

编辑:这将有助于了解更多你正在尝试做什么和你正在处理多少数据。

尝试如下:

  • 使用一种缓冲。例如,使用带有yield return的枚举器一次插入几千个条目。阅读示例:

    public IEnumerable> ReadFileByLines(string cs, int Buffer = Buffer){

        OdbcConnection conn = new OdbcConnection(cs);
        OdbcCommand cmd = new OdbcCommand(sql, conn);
        OdbcDataReader reader = cmd.ExecuteReader();
    
        List<string[]> bufferList = new List<string[]>(Buffer);
        while (reader.Read())
        {
            bufferList.Add(reader["something"]); // or add a custom class here
            if (bufferList.Count == Buffer)
            {
                yield return bufferList.ToArray();
                bufferList = new List<string[]>(Buffer);
            }
        }
        yield return bufferList.ToArray();
    }
    

    …或者创建一个存储过程,它将作为分页机制提供服务,但这将创建许多到数据库的往返。无论哪种方式,读取问题都存在,如果你要读取180万行,你必须以这种或那种方式读取它们。

  • 使用BulkInsert将大量数据一次插入到数据库。
  • 删除GC.Collect()