提高调用 T-SQL 存储过程的 while 循环(C# 代码)的性能
Improving performance of a while loop (C# code) calling a T-SQL stored procedure
当存储过程 SelectNewObjects returns 很少(~500)条记录时,以下 SqlCommand 工作正常,没有明显的性能问题。但是,当它 returns 超过 1,000 条记录时,我开始遇到性能问题:
using (SqlCommand cmdAddNewObject = new SqlCommand("SelectNewObjects", con))
{
cmdAddNewObject.CommandType = CommandType.StoredProcedure;
cmdAddNewObject.Parameters.AddWithValue("@parameter1", parameter1);
using (SqlDataReader rdrAddNewObject = cmdAddNewObject.ExecuteReader())
{
while (rdrAddNewObject.Read())
{
if (rdrAddNewObject.GetString(0) != null)
{
try
{
addObject(parameter1, rdrAddNewObject.GetString(0), rdrAddNewObject.GetString(0).Length / 4, rdrAddNewObject.GetString(0).Substring(0, 2),
rdrAddNewObject.GetString(0).Substring(2, 2), rdrAddNewObject.GetString(1));
if (rdrAddNewObject.GetString(1) == "No description found")
{
// Do something
}
else
{
// Do something else
}
}
catch (Exception ex)
{
// Throw exception
}
}
}
}
}
简而言之,我在这里所做的是为 SelectNewObjects 返回的每条记录调用一个方法 (addObject)。
几个细节:
SelectNewObjects returns 来自临时 table 的记录,创建于 Table1.
在 if / else 块中我正在更新临时中相关记录的 Table1 的一个字段table 在 addObject 执行完成后。
addObject 方法通过使用 SOA(面向服务的体系结构)将在 SQL 临时 table 中找到的每条记录插入到单独的 Oracle 系统中。我没有被授予访问底层数据库的权限,所以我不得不接受这种做事方式。
有时 table 预计会有超过 20,000 条记录(每个只有两个字段:Name 和 描述) 所以这很快就会变成一场噩梦。
在某一时刻,应用程序池将失败并出现以下两个错误(取自 IIS 7 Windows 事件查看器):
事件 ID 5013: 服务于应用程序池“.NET v4.5”的进程在关闭期间超出了时间限制。进程 ID 为“5616”.
事件 ID 5138: 为应用程序池“.NET v4.5”提供服务的工作进程“5616”未能停止侦听器协议 'http' 的频道
分配的时间。数据字段包含错误编号.
technet.microsoft.com 和 support.microsoft.com 中对这些事件 ID 的描述并没有过多地说明此性能问题,也没有确定其根本原因。
也就是说,有没有什么方法可以改进 C# 代码以处理 SP 返回的数万条记录,并更快地对每条记录执行操作?
我假设这是因为通过线路插入 oracle DB 需要很长时间,并且 SQL 连接保持打开的时间比它应该打开的时间长。因此,您需要将数据存储在临时集合中,然后循环遍历列表或批量发送(如果您这样做,则需要将其分解,否则会达到限制)
例如
List<ObjectToStoreInOracle> items = new List<ObjectToStoreInOracle>();
using (SqlCommand cmdAddNewObject = new SqlCommand("SelectNewObjects", con))
{
cmdAddNewObject.CommandType = CommandType.StoredProcedure;
cmdAddNewObject.Parameters.AddWithValue("@parameter1", parameter1);
using (SqlDataReader rdrAddNewObject = cmdAddNewObject.ExecuteReader())
{
while (rdrAddNewObject.Read())
{
if (rdrAddNewObject.GetString(0) != null)
{
try
{
// add items to temp array list
items.Add(new ObjectToStoreInOracle(parameter1, rdrAddNewObject.GetString(0), rdrAddNewObject.GetString(0).Length / 4, rdrAddNewObject.GetString(0).Substring(0, 2), rdrAddNewObject.GetString(0).Substring(2, 2), rdrAddNewObject.GetString(1))))
if (rdrAddNewObject.GetString(1) == "No description found")
{
// Do something
}
else
{
// Do something else
}
}
catch (Exception ex)
{
// Throw exception
}
}
}
}
}
AddToOracleDB(items)
private void AddToOracleDB(List<ObjectToStoreInOracle> items){
//do stuff here to add to the Oracle DB
}
当存储过程 SelectNewObjects returns 很少(~500)条记录时,以下 SqlCommand 工作正常,没有明显的性能问题。但是,当它 returns 超过 1,000 条记录时,我开始遇到性能问题:
using (SqlCommand cmdAddNewObject = new SqlCommand("SelectNewObjects", con))
{
cmdAddNewObject.CommandType = CommandType.StoredProcedure;
cmdAddNewObject.Parameters.AddWithValue("@parameter1", parameter1);
using (SqlDataReader rdrAddNewObject = cmdAddNewObject.ExecuteReader())
{
while (rdrAddNewObject.Read())
{
if (rdrAddNewObject.GetString(0) != null)
{
try
{
addObject(parameter1, rdrAddNewObject.GetString(0), rdrAddNewObject.GetString(0).Length / 4, rdrAddNewObject.GetString(0).Substring(0, 2),
rdrAddNewObject.GetString(0).Substring(2, 2), rdrAddNewObject.GetString(1));
if (rdrAddNewObject.GetString(1) == "No description found")
{
// Do something
}
else
{
// Do something else
}
}
catch (Exception ex)
{
// Throw exception
}
}
}
}
}
简而言之,我在这里所做的是为 SelectNewObjects 返回的每条记录调用一个方法 (addObject)。
几个细节:
SelectNewObjects returns 来自临时 table 的记录,创建于 Table1.
在 if / else 块中我正在更新临时中相关记录的 Table1 的一个字段table 在 addObject 执行完成后。
addObject 方法通过使用 SOA(面向服务的体系结构)将在 SQL 临时 table 中找到的每条记录插入到单独的 Oracle 系统中。我没有被授予访问底层数据库的权限,所以我不得不接受这种做事方式。
有时 table 预计会有超过 20,000 条记录(每个只有两个字段:Name 和 描述) 所以这很快就会变成一场噩梦。
在某一时刻,应用程序池将失败并出现以下两个错误(取自 IIS 7 Windows 事件查看器):
事件 ID 5013: 服务于应用程序池“.NET v4.5”的进程在关闭期间超出了时间限制。进程 ID 为“5616”.
事件 ID 5138: 为应用程序池“.NET v4.5”提供服务的工作进程“5616”未能停止侦听器协议 'http' 的频道 分配的时间。数据字段包含错误编号.
technet.microsoft.com 和 support.microsoft.com 中对这些事件 ID 的描述并没有过多地说明此性能问题,也没有确定其根本原因。
也就是说,有没有什么方法可以改进 C# 代码以处理 SP 返回的数万条记录,并更快地对每条记录执行操作?
我假设这是因为通过线路插入 oracle DB 需要很长时间,并且 SQL 连接保持打开的时间比它应该打开的时间长。因此,您需要将数据存储在临时集合中,然后循环遍历列表或批量发送(如果您这样做,则需要将其分解,否则会达到限制)
例如
List<ObjectToStoreInOracle> items = new List<ObjectToStoreInOracle>();
using (SqlCommand cmdAddNewObject = new SqlCommand("SelectNewObjects", con))
{
cmdAddNewObject.CommandType = CommandType.StoredProcedure;
cmdAddNewObject.Parameters.AddWithValue("@parameter1", parameter1);
using (SqlDataReader rdrAddNewObject = cmdAddNewObject.ExecuteReader())
{
while (rdrAddNewObject.Read())
{
if (rdrAddNewObject.GetString(0) != null)
{
try
{
// add items to temp array list
items.Add(new ObjectToStoreInOracle(parameter1, rdrAddNewObject.GetString(0), rdrAddNewObject.GetString(0).Length / 4, rdrAddNewObject.GetString(0).Substring(0, 2), rdrAddNewObject.GetString(0).Substring(2, 2), rdrAddNewObject.GetString(1))))
if (rdrAddNewObject.GetString(1) == "No description found")
{
// Do something
}
else
{
// Do something else
}
}
catch (Exception ex)
{
// Throw exception
}
}
}
}
}
AddToOracleDB(items)
private void AddToOracleDB(List<ObjectToStoreInOracle> items){
//do stuff here to add to the Oracle DB
}