【问题标题】:Increase performance on OleDB insert into statement, BeginTransaction CommitTransaction提高 OleDB 插入语句 BeginTransaction CommitTransaction 的性能
【发布时间】:2013-08-12 17:47:37
【问题描述】:

我已经编写了将自定义 c# 列表中的数据插入 MSAccess 的附加函数。

第一个只是为每个单独的记录集建立一个新的连接:

        public static void appenddatatotable(string connectionstring, string tablename, string[] values)
    {

            var myconn = new OleDbConnection(connectionstring);


            var cmd = new OleDbCommand();
            cmd.CommandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(@RunDate, @ReportingGroup, @Tariff, @Year)";

            cmd.Parameters.AddRange(new[] { new OleDbParameter("@RunDate", values[0]), new OleDbParameter("@ReportingGroup", values[1]), new OleDbParameter("@Tariff", values[2]), new OleDbParameter("@Year", values[3])});
            cmd.Connection = myconn;
            myconn.Open();
            cmd.ExecuteNonQuery();
            myconn.Close();

    }

然后,我只需遍历我的值列表并在每次迭代时调用此函数。这工作正常,但速度很慢。

在第二个函数中,我尝试在函数中包含循环并使用 BeginTransction 和 Committransaction:

        public static void appenddatatotable2(string connectionstring, string tablename, string datstr, List<PowRes> values)
    {

        var myconn = new OleDbConnection(connectionstring);
        int icounter = 0;

        var cmd = new OleDbCommand();
        OleDbTransaction trans = null;

        cmd.Connection = myconn;
        myconn.Open();
        foreach (var item in values)
        {
            if (icounter == 0)
            {
                trans = cmd.Connection.BeginTransaction();
                cmd.Transaction = trans;
            }

            cmd.CommandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(@RunDate, @ReportingGroup, @Tariff, @Year)";
            if (string.IsNullOrEmpty(item.yr))
                item.yr = "";

            cmd.Parameters.AddRange(new[] { new OleDbParameter("@RunDate", datstr), new OleDbParameter("@ReportingGroup", item.RG), new OleDbParameter("@Tariff", item.tar), new OleDbParameter("@Year", item.yr)});
            cmd.ExecuteNonQuery();
            icounter++;
            if (icounter >= 500)
            {
                trans.Commit();
                icounter = 0;
            }
        }
        if (icounter > 0)
        {
            trans.Commit();
        }


        myconn.Close();

    }

这也可以正常工作,但速度更慢。

我的代码错了吗?如何加快多次插入?

谢谢!

【问题讨论】:

  • 与问题无关,但非常重要:您应该在此处使用using 语句,用于myconncmd,以确保即使出现问题也能清理它们。可能还有trans,但由于您的不寻常用法(分配迟到等)而变得复杂
  • 谢谢马克。好点,我会清理它!关于为什么第二个可能会更慢的任何想法?
  • @npvh 因为您添加了另一个开销,即事务。
  • @npvh 在访问方面,所有理智的期望都会飞出窗外;然而,我们没有理由期望它会更快,我们应该期望它更慢(我们在事务管理方面增加了开销)
  • 您需要更深入地了解这一点 - 什么是真正的 slow、您的代码或数据库?一旦你知道瓶颈在哪里,你就可以看看如何优化。

标签: c# oledb


【解决方案1】:

没有测试,只是我对您的第二个功能的猜测:您在循环中向同一命令添加了太多参数 - cmd.Parameters 在每次使用之前从未被清除..

通常在一个连接中提交大量命令比在单个连接中逐个执行要快得多。

另一种加快插入速度的方法是将所有插入语句转储为长文本,用分号分隔,然后一次性触发提交(我不确定 msAccess 是否支持)

编辑:

将更新命令合并为一个文本:

var updates = values.Select(x => string.Format("INSERT INTO myTable ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES({0}, {1}, {2}, {3})",
                datstr, x.RG, x.tar, x.yr))
                .Aggregate((m, n) => m + ";" + n);
cmd.CommandText = update;

虽然这可能存在 sql 注入问题。

【讨论】:

  • 感谢雷克斯。我不得不承认我认为这就是我正在做的事情。如何合并插入语句,然后使用一次提交?
  • 查看组合文本命令的编辑部分。但出于安全原因,将所有参数放入纯文本可能不是一个好习惯......
  • 感谢分析器。但是,如果您查看其他 stackoverflow 问题(正如您所说),您可能会根据变量名称和用户输入完全搞乱字符串 - 因此我宁愿不走这条路。
【解决方案2】:

这应该比所有现有版本快得多

public static void appenddatatotable2(string connectionstring, string tablename, string datstr, List<PowRes> values)
        {
            string commandText = "INSERT INTO " + tablename + " ([RunDate],[ReportingGroup], [Tariff], [Year]) VALUES(@RunDate, @ReportingGroup, @Tariff, @Year)";
            using (var myconn = new OleDbConnection(connectionstring))
            {
                myconn.Open();
                using (var cmd = new OleDbCommand())
                {
                    foreach (var item in values)
                    {
                        cmd.CommandText = commandText;
                        cmd.Parameters.Clear();
                        cmd.Parameters.AddRange(new[] { new OleDbParameter("@RunDate", datstr), new OleDbParameter("@ReportingGroup", item.RG), new OleDbParameter("@Tariff", item.tar), new OleDbParameter("@Year", item.yr) });
                        cmd.Connection = myconn;
                        cmd.Prepare(); 
                        cmd.ExecuteNonQuery();
                    }
                }   
            }
        }

【讨论】:

  • 也许值得记录一下为什么这样可以更快地澄清。
  • @Ehsan:感谢您的 asnwer。我看到这很节省,因为它按照 Marc 的建议使用 using。但它似乎与我的第一个版本没有什么不同,因为它单独连接并执行每个项目。还是我不明白?
  • 实际上它是您的 2 个版本的组合。这里连接只打开一次,同样的命令只创建一次。它只需要访问所花费的时间
  • @Ehsan:知道了,谢谢。但是仍然可以提高性能,也可以一次写入 DB,而不是逐行写入。没有?
  • 好吧,我在代码中添加了一项准备命令的调整
猜你喜欢
  • 1970-01-01
  • 2017-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-24
  • 1970-01-01
  • 2019-10-29
  • 2016-01-05
相关资源
最近更新 更多