7

我正在处理现有的应用程序。该应用程序从一个巨大的文件中读取数据,然后在进行一些计算后,将数据存储在另一个表中。

但是这样做的循环(见下文)需要很长时间。由于该文件有时包含 1,000 条记录,因此整个过程需要数天时间。

我可以foreach用别的东西代替这个循环吗?我尝试使用Parallel.ForEach,它确实有帮助。我是新手,所以会感谢你的帮助。

foreach (record someredord Somereport.r)
{
    try
    {
        using (var command = new SqlCommand("[procname]", sqlConn))
        {
            command.CommandTimeout = 0;
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(…);

            IAsyncResult result = command.BeginExecuteReader();
            while (!result.IsCompleted)
            {
                System.Threading.Thread.Sleep(10);
            }
            command.EndExecuteReader(result);
        }
    }
    catch (Exception e)
    {
        …
    }
}

查看答案后,我删除了 Async 并使用如下编辑代码。但这并没有提高性能。

using (command = new SqlCommand("[sp]", sqlConn))
{
    command.CommandTimeout = 0;
    command.CommandType = CommandType.StoredProcedure;
    foreach (record someRecord in someReport.)
    {
        command.Parameters.Clear();
        command.Parameters.Add(....)
        command.Prepare();                            

        using (dr = command.ExecuteReader())
        {
            while (dr.Read())
            {
                if ()
                {

                }
                else if ()
                {

                }
            }
        }                             
    }                        
}
4

6 回答 6

8

有没有考虑从 sql server 中提取整组数据并通过数据集处理数据,而不是循环多次 sql 连接?

编辑:决定进一步解释我的意思..您可以执行以下操作,伪代码如下

  1. 使用 select * 并从数据库中获取所有信息并将它们存储到类或字典的列表中。
  2. 做你的 foreach(在 someReport 中记录 someRecord)并像往常一样进行条件匹配。
于 2012-08-30T17:20:58.843 回答
6

第 1 步:放弃异步尝试。它没有正确实施,无论如何你都在阻止。所以只需执行该过程,看看是否有帮助。

第 2 步:将 SqlCommand 移出循环并在每次迭代中重用它。这样您就不会为循环中的每个项目产生创建和销毁它的成本。

警告:确保您从上一次迭代中重置/清除/删除不需要的参数。我们使用可选参数做了类似的事情,并且从上一次迭代中“流血”,因为我们没有清理我们不需要的参数!

于 2012-08-30T17:23:30.043 回答
3

你最大的问题是你正在循环这个:

IAsyncResult result = command.BeginExecuteReader();

while (!result.IsCompleted)
{
   System.Threading.Thread.Sleep(10);
}

command.EndExecuteReader(result);

异步模型的整个想法是调用线程(执行此循环的线程)应该使用 Begin 方法启动所有异步任务,然后再开始使用 End 方法处理结果。如果您在主调用线程中使用 Thread.Sleep() 来等待异步操作完成(就像您在这里一样),那么您做错了,最终发生的是每个命令,一次一个, 正在被调用,然后在下一个开始之前等待。

相反,尝试这样的事情:

public void BeginExecutingCommands(Report someReport)
{
    foreach (record someRecord in someReport.r) 
    {
        var command = new SqlCommand("[procname]", sqlConn);

        command.CommandTimeout = 0;
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(…);

        command.BeginExecuteReader(ReaderExecuted, 
            new object[] { command, someReport, someRecord });                   
    }
}

void ReaderExecuted(IAsyncResult result)
{
    var state = (object[])result.AsyncState;
    var command = state[0] as SqlCommand;
    var someReport = state[1] as Report;
    var someRecord = state[2] as Record;

    try
    {
        using (SqlDataReader reader = command.EndExecuteReader(result))
        {
            // work with reader, command, someReport and someRecord to do what you need.
        }
    }
    catch (Exception ex)
    {
        // handle exceptions that occurred during the async operation here
    }
}
于 2012-08-30T17:39:08.380 回答
1

正如我们在评论中所说,将这些数据存储在内存中并使用它可能是一种更有效的方法。

因此,一种简单的方法是从实体框架开始。Entity Framework 将根据您的数据库模式自动为您生成类。然后,您可以导入包含 SELECT 语句的存储过程。我建议将存储过程导入 EF 的原因是,这种方法通常比在 LINQ 中针对 EF 进行查询更有效。

然后运行存储过程并将数据存储在List这样的...

var data = db.MyStoredProc().ToList();

然后你可以用它做任何你想做的事情data。或者正如我提到的,如果您要对主键进行大量查找,请使用ToDictionary()类似这样的东西......

var data = db.MyStoredProc().ToDictionary(k => k.MyPrimaryKey);

无论哪种方式,此时您都将使用您data的内存。

于 2012-08-30T21:40:42.790 回答
1

在 SQL 中,写入的另一端是一个(一个)磁盘。你很少能并行写得更快。事实上,由于索引碎片,并行通常会减慢它的速度。如果您可以在加载之前按主(集群)键对数据进行排序。在大负载甚至禁用其他键时,加载数据重建键。

不太确定异步中在做什么,但可以肯定它没有按照您的预期做,因为它正在等待自己。

try
{
    using (var command = new SqlCommand("[procname]", sqlConn))
    {
        command.CommandTimeout = 0;
        command.CommandType = CommandType.StoredProcedure;

        foreach (record someredord Somereport.r)
        {
            command.Parameters.Clear()
            command.Parameters.Add(…);

            using (var rdr = command.ExecuteReader())
            {
                while (rdr.Read())
                {
                    …
                }
            }
        }
    }
}
catch (…)
{
    …
}
于 2012-08-30T17:41:42.420 回答
0

似乎执行您的SQL命令会锁定一些必需的资源,这就是强制您使用Async方法的原因(我的猜测)。

如果数据库未在使用中,请尝试对其进行独占访问。即便如此,由于数据模型的复杂性存在一些内部事务,请考虑咨询数据库设计人员。

于 2012-08-30T17:40:04.470 回答