【问题标题】:Dynamically create CSV file using select statement in c#在 C# 中使用 select 语句动态创建 CSV 文件
【发布时间】:2017-02-21 11:23:22
【问题描述】:

我有一个网站,允许用户使用特定搜索条件(国家和日期)将数据下载到 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().Replace(",", ";") + ',';

如果我使用 Response.Write...,文件下载速度会 MUCH 快,但数据都在一行中且不包含标题。 CSV += row... 慢得多,偶尔会超时

如果我要使用 Response.Write 并包含标题,如何将数据分成多行?

实际的 SQL 查询需要 2 秒才能运行并产生 12231 条记录

【问题讨论】:

  • 像这样使用字符串生成器 ... > StringBuilder SB = new StringBuilder();... SB.Append(row[column.ColumnName].ToString().Replace(",", " ;")); SB.Append(','); ...字符串生成器要快得多

标签: c# mysql csv


【解决方案1】:

是的,字符串连接非常慢,因为 C# 中的字符串是不可变的。我建议你使用 StringBuilder 类 (https://msdn.microsoft.com/en-us/library/system.text.stringbuilder(v=vs.110).aspx) 来构造你的字符串。

【讨论】:

    【解决方案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-squared) 时间内运行。当 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-squared) 问题。

    最后,您应该查找 SqlDataReader 类。

    【讨论】:

    • 非常感谢,我明白为什么现在花了这么长时间。精彩的解释,代码有效! :)
    猜你喜欢
    • 2021-01-28
    • 1970-01-01
    • 2013-12-26
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 2019-06-18
    • 1970-01-01
    • 2020-08-05
    相关资源
    最近更新 更多