我有一个SQLite数据库,它有一个包含1800万行和24列的表。我在C#中编写了一个SQL查询函数,我用ExcelDNA将其暴露给Excel。
这是完整的代码,
string constr = constr = "Data Source=" + FilePath + ";Version=3;Synchronous=OFF;temp_store=memory;cache_size=700000;count_changes=off;";
DataTable dt = new DataTable();
try
{
SQLiteConnection conn = new SQLiteConnection(constr);
SQLiteCommand command = new SQLiteCommand(SQLStatement,conn);
conn.Open();
SQLiteDataAdapter sda = new SQLiteDataAdapter(command);
sda.Fill(dt);
sda.Dispose();
command.Dispose();
conn.Dispose();
int numRows = (IncludeHeaders ? dt.Rows.Count + 1 : dt.Rows.Count);
object[,] ret = new object[numRows, dt.Columns.Count];
int rowCount = 0;
if (IncludeHeaders)
{
int colCount = 0;
foreach (DataColumn col in dt.Columns)
{
ret[rowCount, colCount] = col.ColumnName;
colCount++;
}
}
rowCount = (IncludeHeaders ? 1 : 0);
foreach (DataRow row in dt.Rows)
{
int colCount = 0;
foreach (DataColumn col in dt.Columns)
{
if (row[col] != DBNull.Value)
ret[rowCount, colCount] = row[col];
else
ret[rowCount, colCount] = "";
colCount++;
}
rowCount++;
}
return ret;
}
catch (Exception ex)
{
object[,] err = new object[1, 1];
err[0, 0] = ex.ToString();
return err;
}
finally
{
dt.Clear();
dt.Dispose();
dt = null;
}
如果我运行两次查询(两次点击Shift + F9),我会得到一个OutOfMemoryException。在任务管理器中,我可以看到EXCEL.EXE映像的工作集(内存)在抛出异常之前从200MB变为1500MB。
但是,这种行为并不完全一致。我返回5列和1100行的其他查询工作得很好。我在任务管理器中看到内存使用情况,并且一旦结果返回到Excel,我就会看到内存重新出现。
调试上面的应用程序会显示它在sda.Fill(dt)
行前进。
会不会有任何想法?我会更好地使用SQLiteDataReader
吗?或者我可以使用任何其他提示或技巧?谢谢。
顺便说一句,如果我通过Python运行确切的查询,我不会遇到这个问题,所以我认为它与C#中的垃圾收集有关。
以下是有关数据库和查询的一些详细信息。架构沿着 p>行
Date (VARCHAR)
CompanyName (VARCHAR)
Amount (REAL)
AggCode (VARCHAR)
Level1 ... Level20 (VARCHAR)
通常会在Level9
子句中合并字段Level5
,AggCode
,Date
,CompanyName
,WHERE
来运行查询。因此除了原始表之外,我还配置了以下四个索引,
CREATE INDEX idx1 on my(Level09, AggCode);
CREATE INDEX idx2 on my(Level05, AggCode);
CREATE INDEX idx3 on my(CompanyName, AggCode);
CREATE INDEX idx4 on my(Date, AggCode);
成功返回1100行和2列的查询
SELECT CompanyName, SUM(Amount) FROM my where Level09="T_EU" and AggCode = "R_S_V" GROUP BY CompanyName ORDER BY SUM(Amount) DESC
抛出内存异常的查询
SELECT Date, CompanyName, Sum(Amount) FROM my WHERE Level05 ="M_TO" AND AggCode = "C_DTA" GROUP BY Date, CompanyName
第二个查询在Python中返回163行和3列。
异常的完整堆栈跟踪如下,
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
,那么它会落在ExecuteReader()
命令
System.Data.SQLite.SQLiteException (0x80004005): 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
答案 0 :(得分:0)
您的代码没有任何可能导致OutOfMemoryException的内容。因此可能是巨大的数据表和内存不能立即恢复。一种可能性是 - 当你正在阅读excel时...它最后可能有空行,这可能会增加dt大小......
由于数据表是托管对象,因此调用dispose实际上并没有帮助..但是,一旦使用结束,你可以在每次清除dt时使用它。