我有一个网站,允许用户使用特定搜索条件(国家/地区和日期)将数据下载到CSV文件。他们从下拉列表和JQuery日期选择器中选择所有信息。
导出适用于小型数据集,但只要您尝试下载10000行数据,就需要很长时间,甚至可能超时!
我已尝试过不同的方法,但由于此项目的时间限制,我想坚持使用此方法。
我的代码如下:
using (SqlConnection con = new SqlConnection(cs))
{
if (country.Contains("GB"))
{
using (SqlCommand cmd = new SqlCommand("SELECT * from " + logData + " where country in ('GB') and close_date between '" + fromDate + "' and '" + toDate + "'"))
{
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
//Build the CSV file data as a Comma separated string.
string csv = string.Empty;
foreach (DataColumn column in dt.Columns)
{
//Add the Header row for CSV file.
csv += column.ColumnName + ',';
}
//Console.Write(cmd);
//Add new line.
csv += "\r\n";
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
//Add the Data rows.
//Response.Write(row[column.ColumnName].ToString().Replace(",", ";") + ',');
csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
}
//Add new line.
csv += "\r\n";
}
//Download the CSV file.
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=data.csv");
Response.Charset = "";
Response.ContentType = "application/text";
Response.Output.Write(csv);
Response.Flush();
Response.End();
}
}
}
}
Response.Write(row [column.ColumnName] .ToString()。Replace(",",";")+',' );
csv + = row [column.ColumnName] .ToString()。替换(",",";")+',&#39 ;;
如果我使用 Response.Write ...,文件下载速度 MUCH 更快,但数据全部在一行而且不包含标题。 CSV + =行...慢得多,偶尔会超时
如果我要使用Response.Write并包含标题,如何将数据分成多行?
实际的SQL查询需要2秒才能运行并生成12231条记录
答案 0 :(得分:4)
是的,字符串连接非常慢,因为C#中的字符串是不可变的。我建议你使用StringBuilder类(https://msdn.microsoft.com/en-us/library/system.text.stringbuilder(v=vs.110).aspx)来构造你的字符串。
答案 1 :(得分:2)
看起来你试图将整个结果集(来自SELECT
)保存在RAM中两次。不要忘记SQL的重点基本上是让你使用比你拥有的任何RAM都大的数据集。这并不意味着你必须这样做,但它确实意味着你在设计软件时应该“一次一行”。
无论如何,你将整个结果集加载到RAM中,两次。
一次因为你使用SqlDataAdapter而不是SqlDataReader来检索数据。 SqlDataReader一次拉取一行数据。
再次,因为您试图将整个输出放入单个文本字符串中。这很糟糕(a)因为它需要大量的RAM,而且
(b)因为str = str+whatever
必须复制字符串,删除旧字符串并存储新字符串。所以
string str = null;
foreach (item in someLargeCollection) str = str + item;
以O(n平方)时间运行。当n(集合的大小)大于几百项时,看起来像是超时。
所以,快速修复:
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
}
csv += "\r\n";
Response.Write(csv);
csv = null;
}
这会写出每一行,使csv不会太大,并解决第二个问题。
StringBuilder修复...构建字符串的正确方法:
foreach (DataRow row in dt.Rows)
{
StringBuilder csv = new StringBuilder();
foreach (DataColumn column in dt.Columns)
{
csv.Append(row[column.ColumnName].ToString().Replace(",", ";") + ',');
}
csv.AppendLine();
Response.Write(csv.ToString());
}
StringBuilder在创建文本字符串时会破坏O(n平方)问题。
最后,您应该查找SqlDataReader类。