我在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";
}
所以现在我有了这个巨大的结果字符串。我有数据表。必须有一个更好的方法吗?
感谢。
答案 0 :(得分:19)
你自己走在正确的轨道上。使用带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"]);
}
}
}
答案 1 :(得分:2)
我同意你最好的选择是使用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
的列数据的任何格式将该行写入文本文件。
答案 2 :(得分: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)
答案 3 :(得分:2)
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("{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;
}
答案 4 :(得分:2)
保持原有的方法,这是一个快速的胜利:
不使用String作为临时缓冲区,而是使用StringBuilder。这将允许您使用函数.append(String)
进行连接,而不是使用运算符+=
。
运算符+=
效率特别低,因此如果将其置于循环上并且(可能)重复数百万次,则性能将受到影响。
.append(String)
方法不会破坏原始对象,所以它更快
答案 5 :(得分:1)
使用没有response.Close()
的响应对象至少在某些情况下会导致页面的html写出要写入文件的数据。如果使用Response.Close()
,则可以提前关闭连接并导致生成文件时出错。
建议使用HttpApplication.CompleteRequest()
,但这似乎总是会导致html被写入文件的末尾。
我已经结合响应对象尝试了流,并且在开发环境中取得了成功。我还没有在生产中尝试过它。
答案 6 :(得分:1)
我使用 .CSV 通过 DataReader 从数据库导出数据。在我的项目中,我读了datareader并手动创建.CSV文件。在循环中我读取datareader,对于每一行,我将单元格值附加到结果字符串。对于单独的列,我使用&#34;,&#34;对于单独的行,我使用&#34; \ n&#34;。最后我将结果字符串保存为 result.csv 。
我建议high performance extension。我测试了它并快速导出 600,000行为.CSV。