2

我有一个 SQL 数据阅读器,它从 sql db 表中读取 2 列。一旦完成它的工作,它就会再次开始选择另外 2 列。

我会一口气完成所有工作,但这会带来另一组挑战。

我的问题是该表包含大量数据(大约 300 万行左右),这使得使用整个数据集有点问题。

我正在尝试验证字段值,因此我先提取 ID 列,然后提取其他列之一,并通过验证管道运行列中的每个值,其中结果存储在另一个数据库中。

我的问题是,当读者到达处理一列的末尾时,我需要强制它立即清理使用的每个小内存块,因为此过程使用大约 700MB 并且它有大约 200 列要通过。

如果没有完整的垃圾收集器,我肯定会用完 ram。

有人知道我该怎么做吗?

我正在使用许多小的可重用对象,我的想法是我可以在每个读取周期结束时调用 GC.Collect() 并且这会清除所有内容,不幸的是由于某种原因这没有发生。

好的,我希望这适合,但这是有问题的方法...

void AnalyseTable(string ObjectName, string TableName)
{
    Console.WriteLine("Initialising analysis process for SF object \"" + ObjectName + "\"");
    Console.WriteLine("   The data being used is in table [" + TableName + "]");
    // get some helpful stuff from the databases
    SQLcols = Target.GetData("SELECT Column_Name, Is_Nullable, Data_Type, Character_Maximum_Length FROM information_schema.columns WHERE table_name = '" + TableName + "'");
    SFcols = SchemaSource.GetData("SELECT * FROM [" + ObjectName + "Fields]");
    PickLists = SchemaSource.GetData("SELECT * FROM [" + ObjectName + "PickLists]");

    // get the table definition
    DataTable resultBatch = new DataTable();
    resultBatch.TableName = TableName;
    int counter = 0;

    foreach (DataRow Column in SQLcols.Rows)
    {
        if (Column["Column_Name"].ToString().ToLower() != "id")
            resultBatch.Columns.Add(new DataColumn(Column["Column_Name"].ToString(), typeof(bool)));
        else
            resultBatch.Columns.Add(new DataColumn("ID", typeof(string)));
    }
    // create the validation results table
    //SchemaSource.CreateTable(resultBatch, "ValidationResults_");
    // cache the id's from the source table in the validation table
    //CacheIDColumn(TableName);

    // validate the source table
    // iterate through each sql column
    foreach (DataRow Column in SQLcols.Rows)
    {
        // we do this here to save making this call a lot more later
        string colName = Column["Column_Name"].ToString().ToLower();
        // id col is only used to identify records not in validation
        if (colName != "id")
        {
            // prepare to process
            counter = 0;
            resultBatch.Rows.Clear();
            resultBatch.Columns.Clear();
            resultBatch.Columns.Add(new DataColumn("ID", typeof(string)));
            resultBatch.Columns.Add(new DataColumn(colName, typeof(bool)));

            // identify matching SF col
            foreach (DataRow SFDefinition in SFcols.Rows)
            {
                // case insensitive compare on the col name to ensure we have a match ...
                if (SFDefinition["Name"].ToString().ToLower() == colName)
                {
                    // select the id column and the column data to validate (current column data)
                    using (SqlCommand com = new SqlCommand("SELECT ID, [" + colName + "] FROM [" + TableName + "]", new SqlConnection(ConfigurationManager.ConnectionStrings["AnalysisTarget"].ConnectionString)))
                    {
                        com.Connection.Open();
                        SqlDataReader reader = com.ExecuteReader();

                        Console.WriteLine("   Validating column \"" + colName + "\"");
                        // foreach row in the given object dataset 
                        while (reader.Read())
                        {
                            // create a new validation result row
                            DataRow result = resultBatch.NewRow();
                            bool hasFailed = false;
                            // validate it
                            object vResult = ValidateFieldValue(SFDefinition, reader[Column["Column_Name"].ToString()]);
                            // if we have the relevant col definition lets decide how to validate this value ...
                            result[colName] = vResult;

                            if (vResult is bool)
                            {
                                // if it's deemed to have failed validation mark it as such
                                if (!(bool)vResult)
                                    hasFailed = true;
                            }

                            // no point in adding rows we can't trace
                            if (reader["id"] != DBNull.Value && reader["id"] != null)
                            {
                                // add the failed row to the result set
                                if (hasFailed)
                                {
                                    result["id"] = reader["id"];
                                    resultBatch.Rows.Add(result);
                                }
                            }

                            // submit to db in batches of 200
                            if (resultBatch.Rows.Count > 199)
                            {
                                counter += resultBatch.Rows.Count;
                                Console.Write("   Result batch completed,");
                                SchemaSource.Update(resultBatch, "ValidationResults_");
                                Console.WriteLine("      committed " + counter.ToString() + " fails to the database so far.");
                                Console.SetCursorPosition(0, Console.CursorTop-1);
                                resultBatch.Rows.Clear();
                            }
                        }
                        // get rid of these likely very heavy objects
                        reader.Close();
                        reader.Dispose();
                        com.Connection.Close();
                        com.Dispose();
                        // ensure .Net does a full cleanup because we will need the resources.
                        GC.Collect();

                        if (resultBatch.Rows.Count > 0)
                        {
                            counter += resultBatch.Rows.Count;
                            Console.WriteLine("   All batches for column complete,");
                            SchemaSource.Update(resultBatch, "ValidationResults_");
                            Console.WriteLine("      committed " + counter.ToString() + " fails to the database.");
                        }
                    }
                }
            }
        }

        Console.WriteLine("   Completed processing column \"" + colName + "\"");
        Console.WriteLine("");
    }

    Console.WriteLine("Object processing complete.");
}
4

2 回答 2

3

你能发布一些代码吗?.NET 的数据读取器应该是在 RAM 上吝啬的“消防水管”,除非正如 Freddy 所建议的那样,您的列数据值很大。这个验证+数据库写入需要多长时间?

一般来说,如果需要GC并且可以完成,它就会完成。我可能听起来像是一张破唱片,但如果你必须使用 GC.Collect() ,那还有其他问题。

于 2010-05-26T16:29:20.317 回答
1

使用顺序访问打开阅读器,它可能会为您提供所需的行为。此外,假设这是一个 blob,你也可以通过分块阅读它来获得更好的效果。

为 DataReader 提供一种方法来处理包含具有较大二进制值的列的行。SequentialAccess 不是加载整个行,而是使 DataReader 能够将数据作为流加载。然后,您可以使用 GetBytes 或 GetChars 方法指定开始读取操作的字节位置,以及返回数据的有限缓冲区大小。

当您指定 SequentialAccess 时,您需要按照它们返回的顺序从列中读取,尽管您不需要读取每一列。一旦您读取了返回的数据流中的某个位置,就无法再从 DataReader 读取该位置处或之前的数据。使用 OleDbDataReader 时,您可以重新读取当前列值,直到读取过去。当使用SqlDataReader 时,您只能读取一个列值一次。

于 2010-05-26T16:22:49.180 回答