我正在处理现有的应用程序。此应用程序从一个巨大的文件中读取数据,然后在进行一些计算后,将数据存储在另一个表中。
但这样做的循环(见下文)需要很长时间。由于该文件有时包含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 ()
{
}
}
}
}
}
答案 0 :(得分:8)
不是多次循环sql连接,而是考虑从sql server中提取整个数据集并通过数据集处理数据?
编辑:决定进一步解释我的意思.. 您可以执行以下伪代码,如下所示
答案 1 :(得分:6)
第1步:在异步时抛弃尝试。它没有正确实现,你无论如何都要阻止。所以只需执行该程序,看看是否有帮助。
步骤2:将SqlCommand移出循环之外,并在每次迭代时重复使用它。这样你就不会为循环中的每个项目产生和销毁它的成本。
警告:确保重置/清除/删除上一次迭代中不需要的参数。我们使用可选参数执行了类似的操作,并且在上一次迭代中使用了'bleed-thru',因为我们没有清理我们不需要的参数!
答案 2 :(得分:3)
你最大的问题是你正在解决这个问题:
IAsyncResult result = command.BeginExecuteReader();
while (!result.IsCompleted)
{
System.Threading.Thread.Sleep(10);
}
command.EndExecuteReader(result);
异步模型的整个想法是,在开始使用End方法处理结果之前,调用线程(执行此循环的线程)应该使用Begin方法启动所有异步任务。如果你在主调用线程中使用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
}
}
答案 3 :(得分:1)
在SQL的另一端写入是一个(一个)磁盘。你很少能并行写得更快。事实上,并行通常会因索引碎片而减慢速度。如果您可以在加载之前按主(群集)密钥对数据进行排序。在很大的负载甚至禁用其他密钥,加载数据重建密钥。
不确定在asynch中正在做什么,但是肯定它没有按照你自己的预期进行操作。
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 (…)
{
…
}
答案 4 :(得分:1)
正如我们在评论中所讨论的那样,将这些数据存储在内存中并使用它可能是一种更有效的方法。
因此,一种简单的方法是从Entity Framework开始。实体框架将根据您的数据库架构自动为您生成类。然后你可以import a stored procedure保存你的SELECT语句。我建议将存储过程导入EF的原因是这种方法通常比在LINQ中对EF进行查询更有效。
然后运行存储过程并将数据存储在这样的List
中......
var data = db.MyStoredProc().ToList();
然后,您可以使用data
执行任何操作。或者正如我所提到的,如果您在主键上进行大量查找,那么请使用ToDictionary()
这样的内容......
var data = db.MyStoredProc().ToDictionary(k => k.MyPrimaryKey);
无论哪种方式,此时您都会在内存中使用data
。
答案 5 :(得分:0)
似乎执行你的SQL
命令会锁定一些必需的资源,这就是强制你使用Async
方法的原因(我猜)。
如果数据库未在使用中,请尝试独占访问它。即便如此,由于数据模型的复杂性,还有一些内部事务会考虑咨询数据库设计人员。