Reputation: 1477
Note: I considered posting this on DBA Exchange first, but considering this a .NET client issue, I thought it was best to ask here first.
I have two functions that are stored in my Oracle 11g development server, which are called using ODP.NET (using Oracle.ManagedDataAccess as opposed to Oracle.DataAccess).
The two functions are lightning fast in SQL Developer (which makes sense, they are simple queries selecting on only ~20,000 records), but performance (measured with System.Diagnostics.Stopwatch) was less than stellar when fired from my C# app using ODP.Net.
Here are the results: (Ignore 'Conversion time and composing time, they are not part of the query process)
Connecting time - GET_TVM_ALL: 00:00:00.0553501
Query time - GET_TVM_ALL: 00:00:05.3467058
Conversion time: 00:00:07.6508273
Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0006773
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0256008
Conversion time: 00:00:03.7280097
Composing time: 00:00:00.0157274
Total Elapsed: 00:00:16.7796351
An execution time of 5 seconds for GET_TVM_ALL is ridiculously high. Ever more surprising is that the second query is much, much faster. This is strange, as it is without a doubt a more complex query on more than 20x the amount of records.
So I switched them around, and this is the result:
Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0573807
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:05.2981962
Conversion time: 00:00:03.6474905
Connecting time - GET_TVM_ALL: 00:00:00.0007322
Query time - GET_TVM_ALL: 00:00:00.0070785
Conversion time: 00:00:07.2473809
Composing time: 00:00:00.0154049
Total Elapsed: 00:00:16.2268687
As you can see, it seems like the first query is always slow, regardless of its content. To prove this, I made a silly dummy function:
CREATE OR REPLACE FUNCTION GET_DUMMY
RETURN SYS_REFCURSOR
AS
-- REFCURSOR to return data
pCursor SYS_REFCURSOR;
BEGIN
OPEN pCursor FOR SELECT 1 FROM DUAL;
RETURN pCursor;
END;
Now, calling that from my code, let's have a look:
Connecting time - GET_DUMMY: 00:00:00.0581149
Query time - GET_DUMMY: 00:00:05.4103165
Conversion time: 00:00:00.0005617
Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0006580
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0759243
Conversion time: 00:00:03.7577602
Connecting time - GET_TVM_ALL: 00:00:00.0000489
Query time - GET_TVM_ALL: 00:00:00.0037654
Conversion time: 00:00:07.5071360
Composing time: 00:00:00.0152159
Total Elapsed: 00:00:16.7819147
So this proves it, the very first query I'm running is ALWAYS slow.
Extra info: I'm opening and closing a new connection for every single function I'm calling.
Here's my helper function by the way:
public static List<T> ExecuteFunction<T>(string strConnection, string strFunction, OracleDbType returnType, List<DataOracleParameter> parameterList) where T : new()
{
Stopwatch watch = new Stopwatch();
using (OracleConnection objConnection = new OracleConnection(strConnection))
{
// Create the command object and set attributes
OracleCommand objCommand = new OracleCommand(strFunction, objConnection);
objCommand.CommandType = CommandType.StoredProcedure;
// Set the return parameter and type
OracleParameter returnValue = new OracleParameter();
returnValue.OracleDbType = returnType;
returnValue.Direction = ParameterDirection.ReturnValue;
objCommand.Parameters.Add(returnValue);
// Set additional parameters
if (parameterList != null && parameterList.Count > 0)
{
foreach (DataOracleParameter parameter in parameterList)
{
OracleParameter inputValue = new OracleParameter();
inputValue.ParameterName = parameter.ParameterName;
inputValue.OracleDbType = parameter.ParameterType;
inputValue.Value = parameter.ParameterValue;
inputValue.Direction = ParameterDirection.Input;
objCommand.Parameters.Add(inputValue);
}
}
// Create a data adapter to use with the data set
OracleDataAdapter dataAdapter = new OracleDataAdapter(objCommand);
// Create and fill the dataset
DataSet dataSet = new DataSet();
watch.Start();
dataAdapter.Fill(dataSet);
watch.Stop();
Console.WriteLine("Query time - {0}: {1}", strFunction, watch.Elapsed);
List<T> valueList = dataSet.Tables[0].ToList<T>();
return valueList;
}
}
Upvotes: 8
Views: 3408
Reputation: 16377
I encountered this today and it reminded me of an issue from a decade ago with Microsoft's Oracle driver. When we used parameters, it was dog slow, but if we converted to literals, it worked as expected. Parameters, I always thought, were a best practice, so this was very confusing to me.
Turns out it was the Microsoft adapter, which even they admitted was garbage. Switching to ODP.net fixed it.
Fast forward to today... I ran into the same phenomenon using Oracle's Managed ODP.net. When I used parameters (AKA the right way), it took FOREVER to even run the query execute.
using (OracleCommand cmd = new OracleCommand(sql, conn))
{
cmd.Parameters.Add("FROM_DATE", fromDate);
cmd.Parameters.Add("DISTRIBUTOR_ID", distributorId);
using (OracleDataReader reader = cmd.ExecuteReader()) // Bottleneck here
{
}
}
When I switched to literals (again, a horrible practice), it ran instantly.
sql = sql.Replace(":DISTRIBUTOR_ID", distributorId.ToString())
.Replace(":FROM_DATE", string.Format("'{0:dd-MMM-yyyy}'", fromDate));
using (OracleCommand cmd = new OracleCommand(sql, conn))
{
using (OracleDataReader reader = cmd.ExecuteReader())
{
}
}
Disappointing... An issue with managed ODP? Fluke? I will not use this as standard practice, but for now, I'm keeping the literals in this particular code. My application controls the values, so it is SQL injection safe.
P.S. I know I should be using Oracle's to_date
and explicit parameter declaration.
Upvotes: 1
Reputation: 476
I have same problem,I rename the iis application pool ,Let the name short,It fixed my problem,Although incomprehensible but It works for me
Upvotes: 0
Reputation: 2450
At first I'd suggest you to tune FetchSize on the OracleCommand object.
Upvotes: 2