【问题标题】:c# / sqlite / out of memory / transaction / csvc#/sqlite/内存不足/事务/csv
【发布时间】:2018-02-27 09:04:59
【问题描述】:

我该如何解决这个问题? 流程如下。

  1. 导入 csv 文件
  2. 向 sqlite 插入数据(100 万行)

异常是“System.Data.SQLite.OutOfMemoryException” 我使用了“beginTransaction”和“Commit” CSV文件内存为118MB 我什么都试过了。但我不能解决这个问题

这是我的源代码。 请帮忙。

OpenFileDialog file = new OpenFileDialog();
file.Filter = "(*.csv)|*.csv";
file.ShowDialog();

string filename = file.FileName;
string fileNameNoEx = Path.GetFileNameWithoutExtension(filename);

for (int j = 0; j < ListBox.Items.Count; j++)
{
    if (fileNameNoEx.Equals(ListBox.Items[j]))
    {
        return;
    }
}

DirectoryInfo di = new DirectoryInfo("C:/sqlite");
FileInfo fi = new FileInfo("C:/sqlite/SQLiteEx.db");
if (!di.Exists)
{
    di.Create();
}
if (!fi.Exists)
{
    SQLiteConnection.CreateFile("C:/sqlite/SQLiteEx.db");
}

SQLiteConnection conn = new SQLiteConnection("data source = C:/sqlite/SQLiteEx.db;PRAGMA synchronous = OFF; PRAGMA journal_mode = WAL;");
conn.Open();
string sql = "";

StreamReader sr = new StreamReader(filename);
int i = 0;

using (SQLiteTransaction tr = conn.BeginTransaction())
{
    while (!sr.EndOfStream)
    {
        string line = sr.ReadLine();
        string[] cols = line.Split(',');
        string fieldName = "";
        string colData = "";

        if (i == 0)
        {
            for (int j = 0; j < cols.Length; j++)
            {
                if (j == 0)
                {
                    fieldName = "'" + cols[0] + "' TEXT";
                }
                else
                {
                    fieldName = fieldName + ",'" + cols[j] + "' TEXT";
                }
            }
            sql = "CREATE TABLE IF NOT EXISTS " + fileNameNoEx + "(" + fieldName + ");";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            int result = command.ExecuteNonQuery();
        }
        else
        {
            for (int j = 0; j < cols.Length; j++)
            {
                if (j == 0)
                {
                    colData = cols[0];
                }
                else
                {
                    colData = colData + "," + cols[j];
                }
            }

            sql = "INSERT INTO " + fileNameNoEx + " VALUES (" + colData + ");";
            SQLiteCommand command = new SQLiteCommand(sql, conn);
            int result = command.ExecuteNonQuery();
        }
        i++;
    }
    tr.Commit();
}
sr.Close();
conn.Close();

【问题讨论】:

  • 您是否有特殊原因要在单个事务中执行所有插入操作?
  • 由于插入操作不依赖任何其他数据库数据,我不确定是否应该有事务。请记住事务是为了存储所有更改以允许在一个最终命令中恢复或提交它们,所以是的,在此之前所有这些都保存在内存中。
  • 有使用sqlcollect数组的原因吗?我相信这是OOM异常的根本原因。尝试删除它并尝试一下。让我知道。仅供参考,您的代码还有其他问题,人们已经在答案部分提出了它。请考虑他们。

标签: c# sqlite csv transactions out-of-memory


【解决方案1】:

我看到许多字符串连接,您的代码生成了 100 万个 SQLLiteCommands

您是否尝试过使用参数创建单个SQLLiteCommand?您可以更改循环中的参数值(也可以更好地防止 SQL 注入 - 以防受到攻击或 CSV 文件中的 specials 字符)。

例如:

using (SQLiteCommand cmd = cnn.CreateCommand())
{
    cmd.CommandText = @"INSERT INTO [myTable] ([Id], [name]) VALUES (@Id, @Name)";

    cmd.Parameters.Add(new SQLiteParameter("@Id"));
    cmd.Parameters.Add(new SQLiteParameter("@name"));

    foreach(var row in csv)
    {
       cmd.Parameters["@id"].Value = row.id;
       cmd.Parameters["@name"].Value = row.name;

       cmd.ExecuteNonQuery();
    }
}

【讨论】:

  • 我同意您所说的,但这并不能回答问题,并且相关部分仅作为指向外部站点的链接包含在内。这应该是评论,而不是答案(我知道你还没有声誉)。
  • 我明白。尽管如此,尝试使用单个 sqlcommand 可以显着减少所需的内存量。因为实际上,有 100 万个 SQLiteCommand 实例化
  • 这实际上是一个不错的论点 - 它有助于解决当前代码的 SQL 注入漏洞。如果您可以将其纳入您的答案(底部的编辑按钮)并包含链接中的相关代码(代码标记缩进 4 个空格),这可能是一个有效的答案。
  • 编辑完成(不是很容易缩进代码)你有什么比在行首放很多空格更好的方法吗?
  • @Jean-SébastienPadoan 最简单的方法是将代码复制到您选择的编辑器(Notepad++、VS 等)中,然后使用 Tab 或 Shift+Tab 缩进/取消缩进
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 2020-12-23
  • 2011-06-26
  • 2014-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多