13

我在 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";
    }

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

谢谢。

4

8 回答 8

21

你自己走在正确的轨道上。使用循环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"]);
    }
  }
}
于 2012-01-29T18:20:27.350 回答
7

我想出了这个,它是一个比其他答案更好的 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)

于 2014-09-26T14:03:38.717 回答
4

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

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

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;
}
于 2015-12-04T11:57:05.337 回答
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.

于 2012-01-29T18:23:12.183 回答
2

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

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

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

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

于 2016-07-28T10:33:02.000 回答
1

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

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

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

于 2015-12-31T13:21:23.613 回答
1

我使用.CSV通过DataReader从数据库中导出数据。在我的项目中,我阅读了 datareader 并手动创建了 .CSV 文件。在一个循环中,我读取数据读取器,并且对于每一行,我将单元格值附加到结果字符串。对于单独的列,我使用“,”,对于单独的行,我使用“\n”。最后我将结果字符串保存为result.csv

我建议这个高性能扩展。我对其进行了测试并快速将600,000 行导出为 .CSV 。

于 2017-05-23T14:13:05.560 回答
0

我用:

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

您也可以使用此方法来保存 DataSet。

于 2021-09-24T01:09:52.280 回答