8

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;
    }
}
4

3 回答 3

2

首先,我建议您在 OracleCommand 对象上调整FetchSize 。

于 2014-09-07T09:20:53.587 回答
1

我今天遇到了这个问题,它让我想起了十年前微软的 Oracle 驱动程序的一个问题。当我们使用参数时,它很慢,但如果我们转换为文字,它会按预期工作。我一直认为参数是最佳实践,所以这让我很困惑。

原来是微软适配器,连他们都承认是垃圾。切换到 ODP.net 修复了它。

快进到今天...我在使用 Oracle 的 Managed ODP.net 时遇到了同样的现象。当我使用参数(也就是正确的方式)时,甚至需要 FOREVER 才能运行查询执行。

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
    {
    }
}

当我切换到文字时(再次,这是一种可怕的做法),它立即运行。

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())
    {
    }
}

令人失望...托管ODP有问题吗?吸虫?我不会将其用作标准做法,但现在,我将文字保留在此特定代码中。我的应用程序控制这些值,因此它是 SQL 注入安全的。

PS 我知道我应该使用 Oracle 的to_date显式参数声明。

于 2019-05-01T22:15:31.333 回答
0

我有同样的问题,我重命名了 iis 应用程序池,让名称简短,它解决了我的问题,虽然难以理解,但它对我有用

于 2018-07-31T09:48:35.163 回答