如何在c#中有效地从SQL datareader写入文件?

我在C#中有一个远程sql连接,需要执行查询并将其结果保存到用户的本地硬盘。 这个东西可以返回相当大量的数据,因此需要考虑一种有效的存储方式。 我之前已经读过,首先将整个结果放入记忆中,然后编写它并不是一个好主意,所以如果有人可以提供帮助,那就太好了!

我目前正在将sql结果数据存储到DataTable中,虽然我认为它可以更好地在while(myReader.Read(){...}执行某些操作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"; } 

所以现在我有了这个巨大的结果字符串。 我有数据表。 这样做有更好的方法吗?

谢谢。

你自己走在正确的轨道上。 使用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"]); } } } 

我同意你最好的选择是使用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的列数据的任何格式将该行写入文本文件。

Rob Sedgwick的回答更像是它,但可以改进和简化。 这就是我做的方式:

 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 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()); } } } 

你需要一个函数Ge​​tColumnNames:

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

保持原有的方法,这是一个快速的胜利:

不使用String作为临时缓冲区,而是使用StringBuilder 。 这将允许您使用函数.append(String)进行连接,而不是使用运算符+=

运算符+=特别低效,因此如果将其置于循环上并且可能重复(可能)数百万次,性能将受到影响。

.append(String)方法不会破坏原始对象,因此速度更快

使用没有response.Close()的响应对象response.Close()至少在某些情况下导致页面的html写出要写入文件的数据。 如果使用Response.Close() ,则可以提前关闭连接并导致生成文件时出错。

建议使用HttpApplication.CompleteRequest()但这似乎总是导致html被写入文件的末尾。

我已经结合响应对象尝试了流,并在开发环境中取得了成功。 我还没有在生产中尝试过它。

我想出了这个,它是一个比其他答案更好的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通过DataReader数据库导出数据。 在我的项目中,我读了datareader并手动创建.CSV文件。 在循环中,我读取datareader,对于每一行,我将单元格值附加到结果字符串。 对于单独的列,我使用“,”,对于单独的行,我使用“\ n”。 最后我将结果字符串保存为result.csv

我建议这种高性能扩展 。 我测试了它并快速导出600,000行作为.CSV。