Как извлечь, обработать и сохранить огромный набор записей в C # эффективно? - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь достичь следующих вещей:

  • получить данные из базы данных SQL.
  • Передача данных в метод PerformStuff, который имеет сторонний метод MethodforResponse (проверяет ввод и предоставляет ответ)

  • Сохранить ответ (xml) обратно в базу данных SQL.

ниже приведен пример кода. Производительность не очень хорошая, если в БД 1000 000 записей, это очень медленно.

Лучше сделать это? Любая идея или намеки, чтобы сделать это лучше.

, пожалуйста, помогите.

using thirdpartylib;
 public class Program
    {

        static void Main(string[] args)
        {
            var response = PerformStuff();
            Save(response);


        }

        public class TestRequest
        {
            public int col1 { get; set; }
            public bool col2 { get; set; }
            public string col3 { get; set; }
            public bool col4 { get; set; }

            public string col5 { get; set; }
            public bool col6 { get; set; }
            public string col7 { get; set; }

        }
        public class TestResponse
        {
            public int col1 { get; set; }
            public string col2 { get; set; }
            public string col3 { get; set; }
            public int col4 { get; set; }

        }
        public TestRequest GetDataId(int id)
        {
            TestRequest testReq = null;
            try
            {
                SqlCommand cmd = DB.GetSqlCommand("proc_name");
                cmd.AddInSqlParam("@Id", SqlDbType.Int, id);
                SqlDataReader dr = new SqlDataReader(DB.GetDataReader(cmd));
                while (dr.Read())
                {
                    testReq = new TestRequest();

                    testReq.col1 = dr.GetInt32("col1");
                    testReq.col2 = dr.GetBoolean("col2");
                    testReq.col3 = dr.GetString("col3");
                    testReq.col4 = dr.GetBoolean("col4");
                    testReq.col5 = dr.GetString("col5");
                    testReq.col6 = dr.GetBoolean("col6");
                    testReq.col7 = dr.GetString("col7");



                }
                dr.Close();
            }

            catch (Exception ex)
            {
                throw;
            }
            return testReq;

        }
        public static TestResponse PerformStuff()
        {
            var response = new TestResponse();
            //give ids in list
            var ids = thirdpartylib.Methodforid()


            foreach (int id in ids)
            {

                var request = GetDataId(id);


                var output = thirdpartylib.MethodforResponse(request);

                foreach (var data in output.Elements())
                {
                    response.col4 = Convert.ToInt32(data.Id().Class());
                    response.col2 = data.Id().Name().ToString();

                }
            }
            //request details
            response.col1 = request.col1;
            response.col2 = request.col2;
            response.col3 = request.col3;

            return response;
        }

        public static void Save(TestResponse response)
        {

            var Sb = new StringBuilder();
            try
            {
                Sb.Append("<ROOT>");
                Sb.Append("<id");
                Sb.Append(" col1='" + response.col1 + "'");
                Sb.Append(" col2='" + response.col2 + "'");
                Sb.Append(" col3='" + response.col3 + "'");
                Sb.Append(" col4='" + response.col4 + "'");

                Sb.Append("></Id>");
                Sb.Append("</ROOT>");
                var cmd = DB.GetSqlCommand("saveproc");
                cmd.AddInSqlParam("@Data", SqlDbType.VarChar, Sb.ToString());
                DB.ExecuteNoQuery(cmd);

            }
            catch (Exception ex)
            {

                throw;
            }
        }

    }

Спасибо!

Ответы [ 4 ]

0 голосов
/ 11 января 2019

Наблюдение: ваше требование выглядит так, как будто оно соответствует схеме карты / уменьшения.

Если значения в вашей коллекции ids, возвращаемые thirdpartylib.Methodforid(), достаточно плотные, а число строк в таблице за вашей хранимой процедурой proc_name имеет почти такое же количество элементов в коллекции ids вы можете извлечь все нужные записи с помощью одного запроса SQL (и набора результатов из нескольких строк), а не извлекать их по одной. Это может выглядеть примерно так:

public static TestResponse PerformStuff()
{
    var response = new TestResponse();

    var idHash = new HashSet<int> (thirdpartylib.Methodforid());

    SqlCommand cmd = DB.GetSqlCommand("proc_name_for_all_ids");
    using (SqlDataReader dr = new SqlDataReader(DB.GetDataReader(cmd)) { 
        while (dr.Read()) {
            var id = dr.GetInt32("id");
            if (idHash.Contains(id)) {
                testReq = new TestRequest();

                testReq.col1 = dr.GetInt32("col1");
                testReq.col2 = dr.GetBoolean("col2");
                testReq.col3 = dr.GetString("col3");
                testReq.col4 = dr.GetBoolean("col4");
                testReq.col5 = dr.GetString("col5");
                testReq.col6 = dr.GetBoolean("col6");
                testReq.col7 = dr.GetString("col7");

                var output = thirdpartylib.MethodforResponse(request);
                foreach (var data in output.Elements())  {
                    response.col4 = Convert.ToInt32(data.Id().Class());
                    response.col2 = data.Id().Name().ToString();
                }
            } /* end if hash.Contains(id) */  
        }  /* end while dr.Read() */
    } /* end using() */
    return response;
}

Почему это может быть быстрее? Это делает намного меньше запросов к базе данных и вместо этого направляет потоки в несколько строк данных для обработки. Это будет намного эффективнее, чем ваш пример.

Почему это может не сработать?

  1. если значения id должны быть обработаны в том же порядке, который выдает thirdpartylib.Methodforid(), это не сработает.
  2. если нет способа извлечь все строки, то есть нет хранимой процедуры proc_name_for_all_ids, вы не сможете выполнять потоковую передачу строк.
0 голосов
/ 11 января 2019

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

Вам следует подумать о решении, которое: 1. Получает все данные одной команды в базу данных. 2. Обработайте это. 3. Сохраните его обратно в базу данных одной командой, используя метод, подобный BULK INSERT . Помните, что BULK INSERT имеет определенные ограничения, поэтому внимательно прочитайте документацию.

0 голосов
/ 11 января 2019

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

  1. Воспользуйтесь пейджингом на уровне базы данных. Не извлекайте все записи одновременно, чтобы избежать утечек памяти и / или высокого потребления памяти в случаях с 1+ миллионами записей, лучше берите по частям и делайте с ними все, что вам нужно.

  2. Поскольку вам не нужно сохранять XML в базе данных, вы можете сохранить ответ в файл. Сохранение XML в файл дает вам возможность передавать данные на ваш локальный диск.

  3. Вместо того, чтобы собирать XML самостоятельно, используйте XmlSerializer , чтобы выполнить эту работу за вас. XmlSerializer прекрасно работает с XmlWriter , который в итоге может работать с любым потоком , включая FileStream. В нем есть нить , которую вы можете взять в качестве примера.

В заключение, метод PerformStuff будет не только быстрее, но потребует гораздо меньше ресурсов (памяти, ЦП) и, что наиболее важно, вы легко сможете ограничить потребление ресурсов вашей программой (путем изменение размера страницы базы данных).

0 голосов
/ 11 января 2019

Ваш вопрос очень широкий, и метод PerformStuff() будет в корне медленным, потому что требуется O(n) * db_lookup_time до следующей итерации вывода. Так что, мне кажется, вы решаете эту проблему неправильно.

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

Вместо , используйте мощный язык запросов SQL и используйте такие предложения, как where id < 10 and value > 100, потому что вы в конечном итоге хотите ограничить размер набора данных , необходимого для обработки в C #.

Итак:

  1. Чтение просто наименьшее количество данных, которое вам нужно из БД
  2. Обработать эти данные как единое целое, параллелизм может помочь.
  3. Записывать изменения в одном подключении к БД.

Надеюсь, это направит вас в правильном направлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...