Oracle 存储过程 return 游标在 C# 上慢,在 TOAD 上快
Oracle Stored procedure return cursor slow to C#, fast on TOAD
我们使用 asp.net mvc 作为 Web 应用程序,使用 oracle 12C 作为数据库,'Oracle.DataAccess.Client'(12.1.0,64 位版本)用于连接到 Oracle 数据库,我们的 asp.net MVC 应用程序也可以在 64 位机器上运行。
我们的一个存储过程return将游标作为输出参数。
现在,当我们在 TOAD 或 SQL Developer 上执行相同的存储过程时,只需不到一秒,但当 C# 执行相同的存储过程时,大约需要 35 秒..!!!到 return 回来。 TOAD、SQL Developer 和 asp.net iis 服务器都在我的本地机器上,而 Oracle 服务器在不同的机器上。
在 C# 中,我记录了各种计时,例如打开连接花费了多长时间,C# 调用存储过程的时间戳以及它完成和返回的时间,转换花费了多长时间数据集到 C# 列表。在观察所有时间后,我们看到以下语句需要 35 秒
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");//logs time stamp saying start
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);//logs time stamp saying Done
loDataAdapter = null;
我们认为这可能是网络问题,如果是这种情况,蟾蜍和 sql 开发人员也应该同时使用,所以网络不是问题。
我们尝试了什么:
我唯一怀疑的是 ODP.NET 驱动程序,所以删除了所有 oracle 客户端并重新安装了它们,没用。
现在想看看我是否需要在 sqlnet.ora 或 tns 文件连接上配置某些东西?但找不到任何...
这里是完整的代码
public DataTable GetDataTable(字符串 psStoredProcedure)
{
日志信息(psStoredProcedure);
//******************************************************************
//Function.........GetDataTable
//Description......Gets a data set (with the schema) from stored procedure
//Input Param......(1)Stored procedure name
// (2)Boolean to determine if we want to get the schema
//Output...........Return DataTable
//
//******************************************************************
OracleCommand loCommand = new OracleCommand();
OracleConnection loConnection = new OracleConnection(msConnectionString);
DataTable loDataTable = new DataTable();
//---Main Execution Block
try
{
if (!(loConnection.State == ConnectionState.Open))
{
LogInfo("Open Conn");
loConnection.Open();
LogInfo("Open Conn",false);
}
//---Define the SelectCommand
if (moCommand == null)
{
loCommand = new OracleCommand();
}
else
{
loCommand = moCommand;
moCommand = null;
}
loCommand.CommandText = psStoredProcedure;
loCommand.Connection = loConnection;
loCommand.CommandType = CommandType.StoredProcedure;
//---Populate the dataset
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);
loDataAdapter = null;
}
catch (Exception oExcep)
{
throw oExcep;
}
finally
{
//---Close the connection
if (loConnection.State == ConnectionState.Open)
{
LogInfo("Close");
loConnection.Close();
LogInfo("Close", false);
}
loCommand = null;
loConnection = null;
}
LogInfo(psStoredProcedure, false);
return loDataTable;}
以下是PL/SQL存储过程:
CREATE OR REPLACE PROCEDURE REF_LIST_INQ (
ivID IN VARCHAR2 DEFAULT NULL,
ivAID IN VARCHAR2 DEFAULT NULL,
ivDID IN VARCHAR2 DEFAULT NULL,
ivSelection IN VARCHAR2 DEFAULT NULL,
ivStartDate IN VARCHAR2 DEFAULT NULL,
ivEndDate IN VARCHAR2 DEFAULT NULL,
ocRefList OUT ES_PACKAGE.cursorType,
ovReturnCode OUT VARCHAR2,
ovErrorMsg OUT VARCHAR2
)
AS
cmdSQL VARCHAR2(4000) := NULL;
whereCause VARCHAR2(2000) := NULL;
selectCause VARCHAR2(2000) := NULL;
BEGIN
/************
--Hundreds of lines of PL/SQL Code for
-- dynamically creating sql statements
-- and assigning it to 'selectCause' and 'whereCause'
*************************/
cmdSQL := selectCause || whereCause || ' ORDER BY SD.MODIFIED_DATE DESC, SD.REFERRAL_NUMBER';
OPEN ocRefList FOR cmdSQL;
ovReturnCode := '0';
EXCEPTION WHEN OTHERS THEN
ovErrorMsg := 'REF_LIST_INQ - ' || SUBSTR(SQLERRM,1,200);
ovReturnCode := '-1';
END REF_LIST_INQ;
/
对我来说这是一个答案,如果有人面临同样的情况可以尝试一下。
经过几天的研究,以下 C# 语句对我有所帮助。
loCommand.FetchSize = loCommand.FetchSize * 18192;
根据我的理解(我不善于描述,尽我所能)这仅适用于 64 位版本,如果我们不指定 'FetchSize' 它将继续获取预定义的大小(我相信128k) 来自在 Oracle DB 上打开的游标的数据,它将进行往返直到游标提供的数据完成。
这是完整的 C# 代码,但这一行代码有很大不同。
//******************************************************************
//Function.........GetDataTable
//Description......Gets a data set (with the schema) from stored procedure
//Input Param......(1)Stored procedure name
// (2)Boolean to determine if we want to get the schema
//Output...........Return DataTable
//
//******************************************************************
OracleCommand loCommand = new OracleCommand();
OracleConnection loConnection = new OracleConnection(msConnectionString);
DataTable loDataTable = new DataTable();
//---Main Execution Block
try
{
if (!(loConnection.State == ConnectionState.Open))
{
LogInfo("Open Conn");
loConnection.Open();
LogInfo("Open Conn",false);
}
//---Define the SelectCommand
if (moCommand == null)
{
loCommand = new OracleCommand();
}
else
{
loCommand = moCommand;
moCommand = null;
}
loCommand.CommandText = psStoredProcedure;
loCommand.Connection = loConnection;
loCommand.CommandType = CommandType.StoredProcedure;
//Here is the Hero
loCommand.FetchSize = loCommand.FetchSize * 18192;
//---Populate the dataset
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);
loDataAdapter = null;
}
catch (Exception oExcep)
{
throw oExcep;
}
finally
{
//---Close the connection
if (loConnection.State == ConnectionState.Open)
{
LogInfo("Close");
loConnection.Close();
LogInfo("Close", false);
}
loCommand = null;
loConnection = null;
}
LogInfo(psStoredProcedure, false);
return loDataTable;}
我们使用 asp.net mvc 作为 Web 应用程序,使用 oracle 12C 作为数据库,'Oracle.DataAccess.Client'(12.1.0,64 位版本)用于连接到 Oracle 数据库,我们的 asp.net MVC 应用程序也可以在 64 位机器上运行。
我们的一个存储过程return将游标作为输出参数。
现在,当我们在 TOAD 或 SQL Developer 上执行相同的存储过程时,只需不到一秒,但当 C# 执行相同的存储过程时,大约需要 35 秒..!!!到 return 回来。 TOAD、SQL Developer 和 asp.net iis 服务器都在我的本地机器上,而 Oracle 服务器在不同的机器上。
在 C# 中,我记录了各种计时,例如打开连接花费了多长时间,C# 调用存储过程的时间戳以及它完成和返回的时间,转换花费了多长时间数据集到 C# 列表。在观察所有时间后,我们看到以下语句需要 35 秒
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");//logs time stamp saying start
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);//logs time stamp saying Done
loDataAdapter = null;
我们认为这可能是网络问题,如果是这种情况,蟾蜍和 sql 开发人员也应该同时使用,所以网络不是问题。
我们尝试了什么: 我唯一怀疑的是 ODP.NET 驱动程序,所以删除了所有 oracle 客户端并重新安装了它们,没用。
现在想看看我是否需要在 sqlnet.ora 或 tns 文件连接上配置某些东西?但找不到任何...
这里是完整的代码
public DataTable GetDataTable(字符串 psStoredProcedure) { 日志信息(psStoredProcedure);
//******************************************************************
//Function.........GetDataTable
//Description......Gets a data set (with the schema) from stored procedure
//Input Param......(1)Stored procedure name
// (2)Boolean to determine if we want to get the schema
//Output...........Return DataTable
//
//******************************************************************
OracleCommand loCommand = new OracleCommand();
OracleConnection loConnection = new OracleConnection(msConnectionString);
DataTable loDataTable = new DataTable();
//---Main Execution Block
try
{
if (!(loConnection.State == ConnectionState.Open))
{
LogInfo("Open Conn");
loConnection.Open();
LogInfo("Open Conn",false);
}
//---Define the SelectCommand
if (moCommand == null)
{
loCommand = new OracleCommand();
}
else
{
loCommand = moCommand;
moCommand = null;
}
loCommand.CommandText = psStoredProcedure;
loCommand.Connection = loConnection;
loCommand.CommandType = CommandType.StoredProcedure;
//---Populate the dataset
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);
loDataAdapter = null;
}
catch (Exception oExcep)
{
throw oExcep;
}
finally
{
//---Close the connection
if (loConnection.State == ConnectionState.Open)
{
LogInfo("Close");
loConnection.Close();
LogInfo("Close", false);
}
loCommand = null;
loConnection = null;
}
LogInfo(psStoredProcedure, false);
return loDataTable;}
以下是PL/SQL存储过程:
CREATE OR REPLACE PROCEDURE REF_LIST_INQ (
ivID IN VARCHAR2 DEFAULT NULL,
ivAID IN VARCHAR2 DEFAULT NULL,
ivDID IN VARCHAR2 DEFAULT NULL,
ivSelection IN VARCHAR2 DEFAULT NULL,
ivStartDate IN VARCHAR2 DEFAULT NULL,
ivEndDate IN VARCHAR2 DEFAULT NULL,
ocRefList OUT ES_PACKAGE.cursorType,
ovReturnCode OUT VARCHAR2,
ovErrorMsg OUT VARCHAR2
)
AS
cmdSQL VARCHAR2(4000) := NULL;
whereCause VARCHAR2(2000) := NULL;
selectCause VARCHAR2(2000) := NULL;
BEGIN
/************
--Hundreds of lines of PL/SQL Code for
-- dynamically creating sql statements
-- and assigning it to 'selectCause' and 'whereCause'
*************************/
cmdSQL := selectCause || whereCause || ' ORDER BY SD.MODIFIED_DATE DESC, SD.REFERRAL_NUMBER';
OPEN ocRefList FOR cmdSQL;
ovReturnCode := '0';
EXCEPTION WHEN OTHERS THEN
ovErrorMsg := 'REF_LIST_INQ - ' || SUBSTR(SQLERRM,1,200);
ovReturnCode := '-1';
END REF_LIST_INQ;
/
对我来说这是一个答案,如果有人面临同样的情况可以尝试一下。
经过几天的研究,以下 C# 语句对我有所帮助。
loCommand.FetchSize = loCommand.FetchSize * 18192;
根据我的理解(我不善于描述,尽我所能)这仅适用于 64 位版本,如果我们不指定 'FetchSize' 它将继续获取预定义的大小(我相信128k) 来自在 Oracle DB 上打开的游标的数据,它将进行往返直到游标提供的数据完成。
这是完整的 C# 代码,但这一行代码有很大不同。
//******************************************************************
//Function.........GetDataTable
//Description......Gets a data set (with the schema) from stored procedure
//Input Param......(1)Stored procedure name
// (2)Boolean to determine if we want to get the schema
//Output...........Return DataTable
//
//******************************************************************
OracleCommand loCommand = new OracleCommand();
OracleConnection loConnection = new OracleConnection(msConnectionString);
DataTable loDataTable = new DataTable();
//---Main Execution Block
try
{
if (!(loConnection.State == ConnectionState.Open))
{
LogInfo("Open Conn");
loConnection.Open();
LogInfo("Open Conn",false);
}
//---Define the SelectCommand
if (moCommand == null)
{
loCommand = new OracleCommand();
}
else
{
loCommand = moCommand;
moCommand = null;
}
loCommand.CommandText = psStoredProcedure;
loCommand.Connection = loConnection;
loCommand.CommandType = CommandType.StoredProcedure;
//Here is the Hero
loCommand.FetchSize = loCommand.FetchSize * 18192;
//---Populate the dataset
OracleDataAdapter loDataAdapter = new OracleDataAdapter(loCommand);
LogInfo("fill DataTable");
loDataAdapter.Fill(loDataTable);
LogInfo("fill DataTable",false);
loDataAdapter = null;
}
catch (Exception oExcep)
{
throw oExcep;
}
finally
{
//---Close the connection
if (loConnection.State == ConnectionState.Open)
{
LogInfo("Close");
loConnection.Close();
LogInfo("Close", false);
}
loCommand = null;
loConnection = null;
}
LogInfo(psStoredProcedure, false);
return loDataTable;}