【问题标题】:Exporting SqlDataReader results to array in C#将 SqlDataReader 结果导出到 C# 中的数组
【发布时间】:2016-12-09 20:53:23
【问题描述】:

我编写了一个运行 SQL 查询的函数,并通过 ExcelDNA 将它暴露给 Excel。查询本身使用SqlDataAdapter 及其对应的.Fill() 方法来填充DataTable

然后我遍历DataTable 的行和列以填充定义为的二维数组,

object[,] results = new object[dt.Rows.Count, dt.Columns.Count];

然后我可以直接将results 对象返回到 Excel,并且一切都正确呈现(字符串作为字符串,数字作为值)。

但是,我遇到了一个问题,即某些 SQL 查询在调用 .Fill() 方法时会引发“内存不足”异常。

我做了一些研究,结果发现SqlDataReader 可能更有效,因为我感兴趣的只是将数据检索到 Excel 中,它不会将结果集加载到内存中,而是将它们逐行读取行。

我的问题是SqlDataReader 没有.Fill() 方法。我确实有一些工作代码,可以在其中将 SQL 结果输出为 CSV 文件。然后我想我可以编写另一个函数将 CSV 导入 Excel。但这似乎很迂回。

有没有更直接的方法来实现这一点?

下面包含异常的完整堆栈跟踪。

System.Data.SQLite.SQLiteException (0x80004005): out of memory
out of memory
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
at UtilXL.Utils.UtilsSQLite.RunQueryCSLite(String SQLStatement, String FilePath, Boolean IncludeHeaders) in h:\Projects\UtilXL\UtilXL\Utils\UtilsSQLite.cs:line 37

上面引用的第 37 行是 sda.Fill() 调用。

这是运行SqlDataReader时的堆栈跟踪,

System.Data.SQLite.SQLiteException (0x80004005): out of memory out of memory
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteReader()
at UtilXL.Utils.UtilsSQLite.RunQueryCSReader(String SQLStatement, String FilePath, Boolean IncludeHeaders) in h:\Projects\UtilXL\UtilXL\Utils\UtilsSQLite.cs:line 111

【问题讨论】:

  • 如果您改为通过 QueryTables.Add() 将数据添加到 Excel,这不是更容易吗?顺便说一句,尽管它是 SQLite,但它被标记为 SQL 服务器。
  • 谢谢,已更正标签。如果查询的结果只有几行,我不明白为什么填充 DataTable 本身就是一个问题。那些不是发送到 DataTable 的行吗?一些返回更多行(1000+)的查询工作得很好。
  • 某处(可能在您的另一侧),您说的是 1800 万行,现在只有几行!可能“一些”更现实,DataTable 不太可能能够处理这样的负载。我没有看到你的代码,也不知道你的数据。
  • 没有。 1800 万是整个数据库的大小。我正在运行的查询只返回 160 行。所以不知道为什么我会为这么小的结果集遇到 OOM 异常。
  • 那真的很奇怪。从错误输出来看,它与 Excel 无关。它在加载到数据表时中断。您是否尝试过使用 Linq(您可以通过 Nuget 获得 IQToolkit)?

标签: c# excel sqlite sqldatareader excel-dna


【解决方案1】:

我没有使用 ExcelDNA 的经验,但是...

我认为您应该尝试了解为什么您会收到OutOfMemoryException。它经常发生在使用大对象(在您的情况下,可能是字符串)的 32 位应用程序中。

确实,使用SqlDataReader 一次处理一行将比将所有行加载到DataTable 中使用托管堆中的内存更少。但是您需要一次将一行加载到 Excel 中 - 例如来自声明为的数组:

object[,] results = new object[1, reader.FieldCount];

这可能比一次调用加载二维数组要慢。

【讨论】:

  • 谢谢,乔。我不明白为什么会出现内存异常。结果集本身很小,大约 160 行和 3 列。完整的细节在这里stackoverflow.com/questions/41057622/…
  • @insomniac - 也许你的代码有一个导致无限递归的错误。你有 OutOfMemoryException 的堆栈跟踪吗?您是否尝试过使用调试器单步执行您的代码?
  • 刚刚添加了堆栈跟踪。
  • 顺便说一句,我试过SqlDataReader,但没有骰子。也摔倒了。
【解决方案2】:

这不是我认为的答案,否则我不知道如何格式化代码。假设您从 nuget 获得 IQToolkit,则从您的其他步骤中获取您的基本代码,您可以尝试这样的事情吗:

void Main()
{
  System.Data.SQLite.SQLiteConnection con = new System.Data.SQLite.SQLiteConnection( @"Data Source="+FilePath );
  SQLiteQueryProvider provider = new SQLiteQueryProvider(con, new ImplicitMapping(), QueryPolicy.Default);
    var data = provider.GetTable<MyTable>("MyTable")
    .Where(mt => mt.Level1 == "M_TO" && mt.AggCode == "C_DTA")
    .GroupBy(mt => new {mt.Date, mt.CompanyName})
    .Select(mt => new
     {
         Date = mt.Key.Date
         CompanyName = mt.Key.CompanyName,
         Sum = mt.Sum(t => t.Amount)
     }
    ).ToList();

//如果它成功到达这里,那么你的状态很好

  int numRows = data.Count() + (IncludeHeaders ?  1 : 0);
  object[,] ret = new object[numRows, 3];
  // ..
}

//// Entity Class
public class MyTable
{
    public string CompanyName { get; set; }
    public decimal Amount { get; set; }
    public DateTime? Date { get; set; } // varchar, really?
    public string AggCode { get; set; }
    public string Level1 { get; set; }
    //...
    public string Level5 { get; set; }
    // ...
    public string Level20 { get; set; }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-22
    • 1970-01-01
    • 2013-10-20
    相关资源
    最近更新 更多