C #: Принудительный запуск в длительном цикле чтения SQL? - PullRequest
2 голосов
/ 26 мая 2010

У меня есть средство чтения данных SQL, которое читает 2 столбца из таблицы sql db. как только он сделал свой бит, он начинает снова, выбирая еще 2 столбца.

Я бы потянул всю партию за один раз, но это представляет целый ряд других проблем.

Моя проблема в том, что таблица содержит большой объем данных (около 3 миллионов строк или около того), что делает работу со всем набором проблематичной.

Я пытаюсь проверить значения полей, поэтому я вытаскиваю столбец ID, затем один из других столбцов и запускаю каждое значение в столбце через конвейер проверки, где результаты хранятся в другой базе данных.

Моя проблема в том, что, когда читатель достигает конца столбца handlin, мне нужно заставить его немедленно очистить каждый маленький блок памяти, используемый в этом процессе, так как этот процесс использует около 700 МБ и имеет около 200 столбцов для прохождения.

Без полного сбора мусора у меня точно не останется барана.

У кого-нибудь есть идеи, как я могу это сделать?

Я использую множество небольших многократно используемых объектов, я думал, что я мог бы просто вызывать 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.");
}

Ответы [ 3 ]

2 голосов
/ 26 мая 2010

Не могли бы вы опубликовать код?Считыватель данных .NET должен быть «пожарным шлангом», который скуп на ОЗУ, если, как предполагает Фредди, ваши значения данных столбца не велики.Сколько времени занимает проверка и запись в БД?

В общем, если GC необходим и его можно выполнить, он будет выполнен.Я могу звучать как испорченная запись, но если вам нужно GC.Collect (), что-то еще не так.

1 голос
/ 26 мая 2010

Откройте читатель с последовательным доступом , это может дать вам поведение, которое вам нужно. Кроме того, если предположить, что это капля, вам, возможно, будет лучше, если вы будете читать ее порциями.

Предоставляет DataReader способ обрабатывать строки, содержащие столбцы с большими двоичными значениями. Вместо загрузки всей строки SequentialAccess позволяет DataReader загружать данные в виде потока. Затем можно использовать метод GetBytes или GetChars, чтобы указать расположение байта для запуска операции чтения и ограниченный размер буфера для возвращаемых данных.

Когда вы указываете SequentialAccess, вы обязаны читать из столбцов в том порядке, в котором они возвращаются, хотя вам не обязательно читать каждый столбец. После того как вы прочитали местоположение в возвращенном потоке данных, данные в этом месте или до него больше не будут считываться из DataReader. При использовании OleDbDataReader вы можете перечитать текущее значение столбца, пока не прочитаете его. При использовании SqlDataReader вы можете прочитать значение столбца может только один раз.

0 голосов
/ 26 мая 2010

Не могли бы вы попробовать метод, упомянутый в этой статье .

...