Как эффективно писать в файл из SQL datareader на c#?
у меня есть удаленное sql-соединение на C#, которое должно выполнить запрос и сохранить его результаты на локальный жесткий диск пользователей. Существует довольно большой объем данных, которые эта вещь может вернуть, поэтому нужно подумать об эффективном способе его хранения. Я читал раньше, что сначала поместить весь результат в память, а затем написать это не очень хорошая идея, поэтому, если кто-то может помочь, было бы здорово!
в настоящее время я храню данные результата sql в DataTable, хотя я думаю об этом может быть, лучше сделать что-то в while(myReader.Read(){...}
Ниже приведен код, который получает результаты:
DataTable t = new DataTable();
string myQuery = QueryLoader.ReadQueryFromFileWithBdateEdate(@"Resourcesqrsqryssysblo.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 += "rn";
}
Так вот у меня это огромный результат. И у меня есть datatable. Должен быть лучший способ сделать это?
спасибо.
7 ответов
вы сами на правильном пути. Используйте цикл с while(myReader.Read(){...}
и писать каждую запись в текстовый файл внутри цикла. Платформа .NET framework и операционная система позаботятся о эффективной очистке буферов на диск.
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"]);
}
}
}
Я согласен, что лучше всего здесь было бы использовать SqlDataReader
. Что-то вроде этого:--5-->
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
loop, вы можете записать эту строку в текстовый файл в любом формате, который вы считаете подходящим для данных столбца из SqlDataReader
.
ответ Роба Седжвика больше похож на него, но может быть улучшен и упрощен. Вот как я это сделал:--3-->
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;
}
сохраняя свой оригинальный подход, вот быстрый выигрыш:
вместо использования строки в качестве временного буфера используйте StringBuilder. Это позволит вам использовать функцию .append(String)
для конкатенаций вместо использования оператора +=
.
оператор +=
особенно неэффективно, поэтому, если вы поместите его в цикл, и он повторяется (потенциально) миллионы раз, производительность будет затронута.
на .append(String)
способ не уничтожить исходный объект, так что это быстрее
использование объекта response без response.Close()
вызывает, по крайней мере, в некоторых случаях html страницы, записывающей данные для записи в файл. Если вы используете Response.Close()
соединение может быть закрыто преждевременно и вызвать ошибку при создании файла.
рекомендуется использовать HttpApplication.CompleteRequest()
однако это, по-видимому, всегда приводит к записи html в конец файла.
я попробовал поток в сочетании с объектом response и добились успеха в области развития. Я еще не пробовал его в производстве.
Я придумал это, это лучший 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)
Я .CSV экспортировать данные из база данных by DataReader. в моем проекте я читаю datareader и создаю.CSV-файл вручную. в цикле я читаю datareader и для каждой строки добавляю значение ячейки в строку результата. для отдельных столбцов я использую ", "и для отдельных строк я использую"\n". наконец, я сохранил строку результата как результат.csv.
Я предлагаю это расширение высокой эффективности. я проверил его и ... быстро экспортировать 600,000 строк as .CSV .