如何在 C# 中有效地从 SQL 数据读取器写入文件?
Posted
技术标签:
【中文标题】如何在 C# 中有效地从 SQL 数据读取器写入文件?【英文标题】:How to efficiently write to file from SQL datareader in c#? 【发布时间】:2012-02-21 18:06:05 【问题描述】:我在 C# 中有一个远程 sql 连接,需要执行查询并将其结果保存到用户的本地硬盘。这个东西可以返回的数据量相当大,所以需要想一个有效的方法来存储它。我之前读过,首先将整个结果放入内存然后再写它不是一个好主意,所以如果有人可以提供帮助,那就太好了!
我目前正在将 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";
所以现在我有了这个巨大的结果字符串。我有数据表。必须有更好的方法吗?
谢谢。
【问题讨论】:
***.com/questions/2244655/…的可能重复 您只是在写入未格式化的平面文件,还是将数据放入 .csv 电子表格等列中更好? 【参考方案1】:你自己走在正确的轨道上。使用带有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"]);
【讨论】:
谢谢。我应该使用哪种写作方法?流作家? IO?一个例子会很有帮助。谢谢 如果要写入文件,请使用 StreamWriter。请参阅我的更新示例。 谢谢。对于我正在使用的所有行:StringBuilder row = new StringBuilder(); for(int i = 0; i < myReader.FieldCount; i++) row.Append(myReader[i]); if(i!= myReader.FieldCount -1 ) row.Append("\t"); writer.WriteLine(row);
第 9 行缺少右括号。尝试编辑,但少于 6 个字符。
@StevenWhite 谢谢。固定。【参考方案2】:
我想出了这个,它是比其他答案更好的 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)
【讨论】:
您不想在for (int index = 0; index < dataReader.FieldCount - 1; index++)
循环中使用- 1
。它必须在少于计数时运行。如果计数为 1 项,则仅在存在 - 1
时结束,因为 0,index
不是
Rob,我非常感谢这种使用通用导出所有列和行的实现。但是,由于以下几行,我最终得到了列表中最后一个字段的双重导出: if (!dataReader.IsDBNull(dataReader.FieldCount - 1)) sb.Append(dataReader.GetValue(dataReader.FieldCount - 1 ).ToString().Replace(分隔符, " "));你能帮我理解这些行的目的吗?否则,我只会将它们排除在我自己的代码之外......【参考方案3】:
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<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("012", 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("012", 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;
【讨论】:
【参考方案4】:我同意您最好的选择是使用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
中的列数据的格式写入文本文件。
【讨论】:
【参考方案5】:保持你原来的方法,这是一个快速的胜利:
不要使用字符串作为临时缓冲区,而是使用StringBuilder。这将允许您使用函数.append(String)
进行连接,而不是使用运算符+=
。
运算符+=
效率特别低,因此如果将其置于循环中并重复(可能)数百万次,性能将受到影响。
.append(String)
方法不会破坏原始对象,所以速度更快
【讨论】:
【参考方案6】:使用没有response.Close()
的响应对象至少在某些情况下会导致页面的html 写出要写入文件的数据。如果您使用Response.Close()
,连接可能会过早关闭并导致生成文件时出错。
建议使用HttpApplication.CompleteRequest()
,但这似乎总是导致将html写入文件末尾。
我已经尝试将流与响应对象结合使用,并在开发环境中取得了成功。我还没有在生产中尝试过。
【讨论】:
【参考方案7】:我使用 .CSV 通过 DataReader 从 database 导出数据。在我的项目中,我阅读了 datareader 并手动创建了 .CSV 文件。在一个循环中,我读取数据读取器,并且对于每一行,我将单元格值附加到结果字符串。对于单独的列,我使用“,”,对于单独的行,我使用“\n”。最后我将结果字符串保存为 result.csv。
我建议high performance extension。我对其进行了测试并快速将 600,000 行导出为 .CSV。
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 感谢您的评论。我完成了我的答案【参考方案8】:我用:
private void SaveData(string path)
DataTable tblResult = new DataTable();
using(SqlCommand cm = new SqlCommand("select something", objConnect))
tblResult.Load(cm.ExecuteLoad());
if (tblResult != null)
using(FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(fs, tblResult);
易于使用,易于加载,具有:
private DataTable LoadData(string path)
DataTable t = new DataTable();
using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
BinaryFormatter bin = new BinaryFormatter();
t = (DataTable)bin.Deserialize(fs);
return t;
您也可以使用此方法来保存数据集。
【讨论】:
以上是关于如何在 C# 中有效地从 SQL 数据读取器写入文件?的主要内容,如果未能解决你的问题,请参考以下文章