2

SQL db テーブルから 2 つの列を読み取る SQL データ リーダーがあります。そのビットが完了すると、別の 2 つの列の選択が再び開始されます。

私は一度にすべてを引っ張りますが、それはまったく別の一連の課題を提示します.

私の問題は、テーブルに大量のデータ (約 300 万行程度) が含まれているため、セット全体を操作するのが少し面倒なことです。

フィールド値を検証しようとしているので、ID 列を取得してから他の列の 1 つを取得し、結果が別のデータベースに保存される検証パイプラインを介して列の各値を実行しています。

私の問題は、リーダーが 1 つの列の処理の終わりに達したときに、このプロセスが約 700MB を使用し、通過する列が約 200 あるため、使用されている RAM のすべての小さなブロックをすぐに強制的にクリーンアップする必要があることです。

完全なガベージコレクトがなければ、間違いなく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 のデータ リーダーは、Freddy が示唆するように、列データの値が大きい場合を除き、RAM にけちな「消防ホース」であると想定されています。この検証と DB の書き込みにはどのくらいの時間がかかりますか?

一般に、GC が必要で実行できる場合は実行されます。私は壊れたレコードのように聞こえるかもしれませんが、GC.Collect() が必要な場合は、何か他のことが間違っています。

于 2010-05-26T16:29:20.317 に答える
1

Sequential access でリーダーを開くと、必要な動作が得られる場合があります。また、それがブロブであると仮定すると、チャンクで読み取る方が良い場合もあります。

DataReader が大きなバイナリ値を持つ列を含む行を処理する方法を提供します。SequentialAccess は、行全体をロードするのではなく、DataReader がデータをストリームとしてロードできるようにします。次に、GetBytes または GetChars メソッドを使用して、読み取り操作を開始するバイト位置と、返されるデータの制限されたバッファー サイズを指定できます。

SequentialAccess を指定すると、返された順序で列から読み取る必要がありますが、各列を読み取る必要はありません。返されたデータ ストリーム内の場所を超えて読み取ると、その場所またはそれより前のデータを DataReader から読み取ることができなくなります。OleDbDataReader を使用すると、現在の列の値を読み取ってから読み取ることができます。SqlDataReader を使用すると、列の値を一度だけ読み取ることができます。

于 2010-05-26T16:22:49.180 に答える