Как эффективно записать в файл из SQL datareader в c #? - PullRequest
8 голосов
/ 29 января 2012

У меня есть удаленное sql-соединение в C #, которое должно выполнить запрос и сохранить его результаты на локальный жесткий диск пользователя. Эта вещь может вернуть довольно большой объем данных, поэтому необходимо продумать эффективный способ ее хранения. До этого я читал, что сначала помещать весь результат в память, а затем записывать его - не очень хорошая идея, поэтому, если кто-то может помочь, было бы здорово!

В настоящее время я сохраняю данные результата sql в DataTable, хотя думаю, что может быть лучше сделать что-то в while(myReader.Read(){...} Ниже приведен код, который получает результаты:

          DataTable t = new DataTable();
            string myQuery = QueryLoader.ReadQueryFromFileWithBdateEdate(@"Resources\qrs\qryssysblo.q", newdate, newdate);
            using (SqlDataAdapter a = new SqlDataAdapter(myQuery, sqlconn.myConnection))
            {
                a.Fill(t);
            }

            var result = string.Empty;
    for(int i = 0; i < t.Rows.Count; i++)
    {
        for (int j = 0; j < t.Columns.Count; j++)
        {
            result += t.Rows[i][j] + ",";
        }


        result += "\r\n";
    }

Так что теперь у меня есть эта огромная строка результата. И у меня есть данные. Там должен быть гораздо лучший способ сделать это?

Спасибо.

Ответы [ 7 ]

19 голосов
/ 29 января 2012

Вы на правильном пути сами.Используйте цикл с while(myReader.Read(){...} и запишите каждую запись в текстовый файл внутри цикла.Платформа .NET и операционная система позаботятся о том, чтобы буферы были эффективно очищены от диска.

using(SqlConnection conn = new SqlConnection(connectionString))
using(SqlCommand cmd = conn.CreateCommand())
{
  conn.Open();
  cmd.CommandText = QueryLoader.ReadQueryFromFileWithBdateEdate(
    @"Resources\qrs\qryssysblo.q", newdate, newdate);

  using(SqlDataReader reader = cmd.ExecuteReader())
  using(StreamWriter writer = new StreamWriter("c:\temp\file.txt"))
  {
    while(reader.Read())
    {
      // Using Name and Phone as example columns.
      writer.WriteLine("Name: {0}, Phone : {1}", 
        reader["Name"], reader["Phone"]);
    }
  }
}
2 голосов
/ 28 июля 2016

Сохраняя свой оригинальный подход, вот быстрый выигрыш:

Вместо использования String в качестве временного буфера, используйте StringBuilder . Это позволит вам использовать функцию .append(String) для конкатенации вместо оператора +=.

Оператор += особенно неэффективен, поэтому, если вы поместите его в цикл, и он будет повторяться (потенциально) миллионы раз, производительность будет затронута.

Метод .append(String) не уничтожит исходный объект, поэтому он быстрее

2 голосов
/ 04 декабря 2015

Ответ Роба Седжвика больше похож на него, но его можно улучшить и упростить. Вот как я это сделал:

string separator = ";";
string fieldDelimiter = "";
bool useHeaders = true;

string connectionString = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

using (SqlConnection conn = new SqlConnection(connectionString))
{
     using (SqlCommand cmd = conn.CreateCommand())
     {
          conn.Open();
          string query = @"SELECT whatever";

          cmd.CommandText = query;

          using (SqlDataReader reader = cmd.ExecuteReader())
          {
                if (!reader.Read())
                {
                     return;
                }

                List<string> columnNames = GetColumnNames(reader);

                // Write headers if required
                if (useHeaders)
                {
                     first = true;
                     foreach (string columnName in columnNames)
                     {
                          response.Write(first ? string.Empty : separator);
                          line = string.Format("{0}{1}{2}", fieldDelimiter, columnName, fieldDelimiter);
                          response.Write(line);
                          first = false;
                     }

                     response.Write("\n");
                }

                // Write all records
                do
                {
                     first = true;
                     foreach (string columnName in columnNames)
                     {
                          response.Write(first ? string.Empty : separator);
                          string value = reader[columnName] == null ? string.Empty : reader[columnName].ToString();
                          line = string.Format("{0}{1}{2}", fieldDelimiter, value, fieldDelimiter);
                          response.Write(line);
                          first = false;
                     }

                     response.Write("\n");
                }
                while (reader.Read());
          }
     }
}

И вам нужна функция GetColumnNames:

List<string> GetColumnNames(IDataReader reader)
{
    List<string> columnNames = new List<string>();
    for (int i = 0; i < reader.FieldCount; i++)
    {
         columnNames.Add(reader.GetName(i));
    }

    return columnNames;
}
2 голосов
/ 26 сентября 2014

Я придумал это, это лучший писатель CSV, чем другие ответы:

public static class DataReaderExtension
{
    public static void ToCsv(this IDataReader dataReader, string fileName, bool includeHeaderAsFirstRow)
    {

        const string Separator = ",";

        StreamWriter streamWriter = new StreamWriter(fileName);

        StringBuilder sb = null;

        if (includeHeaderAsFirstRow)
        {
            sb = new StringBuilder();
            for (int index = 0; index < dataReader.FieldCount; index++)
            {
                if (dataReader.GetName(index) != null)
                    sb.Append(dataReader.GetName(index));

                if (index < dataReader.FieldCount - 1)
                    sb.Append(Separator);
            }
            streamWriter.WriteLine(sb.ToString());
        }

        while (dataReader.Read())
        {
            sb = new StringBuilder();
            for (int index = 0; index < dataReader.FieldCount; index++)
            {
                if (!dataReader.IsDBNull(index))
                {
                    string value = dataReader.GetValue(index).ToString();
                    if (dataReader.GetFieldType(index) == typeof(String))
                    {
                        if (value.IndexOf("\"") >= 0)
                            value = value.Replace("\"", "\"\"");

                        if (value.IndexOf(Separator) >= 0)
                            value = "\"" + value + "\"";
                    }
                    sb.Append(value);
                }

                if (index < dataReader.FieldCount - 1)
                    sb.Append(Separator);
            }

            if (!dataReader.IsDBNull(dataReader.FieldCount - 1))
                sb.Append(dataReader.GetValue(dataReader.FieldCount - 1).ToString().Replace(Separator, " "));

            streamWriter.WriteLine(sb.ToString());
        }
        dataReader.Close();
        streamWriter.Close();
    }
}

использование: mydataReader.ToCsv ("myfile.csv", true)

2 голосов
/ 29 января 2012

Я согласен, что вам лучше всего использовать SqlDataReader. Как то так:

StreamWriter YourWriter = new StreamWriter(@"c:\testfile.txt");
SqlCommand YourCommand = new SqlCommand();
SqlConnection YourConnection = new SqlConnection(YourConnectionString);
YourCommand.Connection = YourConnection;
YourCommand.CommandText = myQuery;

YourConnection.Open();

using (YourConnection)
{
    using (SqlDataReader sdr = YourCommand.ExecuteReader())
        using (YourWriter)
        {
            while (sdr.Read())
                YourWriter.WriteLine(sdr[0].ToString() + sdr[1].ToString() + ",");

        }
}

Обратите внимание, что в цикле while вы можете записать эту строку в текстовый файл в любом формате, который вы считаете подходящим для данных столбца из SqlDataReader.

1 голос
/ 23 мая 2017

Я использовал .CSV для экспорта данных из базы данных с помощью DataReader .В моем проекте я прочитал datareader и вручную создал файл .CSV.в цикле я читаю datareader и для каждой строки добавляю значение ячейки к результирующей строке.для отдельных столбцов я использую "," и для отдельных строк я использую "\ n".наконец я сохранил строку результата как result.csv .

Я предлагаю это расширение высокой производительности .я проверил это и быстро экспортировал 600 000 строк как .CSV.

1 голос
/ 31 декабря 2015

Использование объекта ответа без response.Close() вызывает, по крайней мере, в некоторых случаях, HTML-код страницы записывает данные для записи в файл. Если вы используете Response.Close(), соединение может быть преждевременно закрыто, что приведет к ошибке при создании файла.

Рекомендуется использовать HttpApplication.CompleteRequest(), однако это всегда приводит к записи html в конец файла.

Я пробовал поток вместе с объектом ответа и добился успеха в среде разработки. Я еще не пробовал в производстве.

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