如何提高存储过程的执行性能?

时间:2014-05-14 01:04:31

标签: c# oracle odp.net

我的任务是从Oracle数据库调用存储过程。 存储过程定义如下:

CREATE OR REPLACE PROCEDURE my_st_proc
   (pname IN VARCHAR2, pdate IN date, o_rc OUT sys_refcursor, o_flag OUT number);

因此它需要两个输入参数并返回两个输出参数,其中一个是游标。 使用以下代码在pl \ sql developer中测试性能时,它会在2到3秒内完成。

DECLARE   
   pname varchar2(300) := 'john doe';  
   pdate date := to_date('01/01/1900','dd/mm/yyyy');

   o_flag number;
   o_data sys_refcursor;

   --MyRec describes the fields, returned by the cursor
   TYPE MyRec IS RECORD 
        (cAccount VARCHAR2(20), cBalance number, cDate date, cCurr varchar2(8));
   rec MyRec;
BEGIN   
    my_st_proc(pname,pdate,o_data,o_flag); 
    dbms_output.put_line(o_flag);

    LOOP
        FETCH o_data INTO rec;
        EXIT WHEN o_data%NOTFOUND;
        dbms_output.put_line(
            rec.cAccount||','||rec.cBalance||','||rec.cDate||','||rec.cCurr);
    END LOOP;

    close o_data;
END;

然而,当我通过ODP.Net调用存储过程时,最多需要两秒钟才能完成(3 - 5秒)。

const string p_name = "pname";
const string p_date = "pdate";
const string p_data = "o_data";
const string p_flag = "o_flag";

using (var connection = new OracleConnection("my connection"))
{
    var command = connection.CreateCommand();
    command.CommandType = CommandType.StoredProcedure;
    command.CommandText = "my_st_proc";

    var pname = command.Parameters.Add(p_name, OracleDbType.Varchar2);
    pname.Direction = ParameterDirection.Input;
    var pdate = command.Parameters.Add(p_date, OracleDbType.Date);
    pdate.Direction = ParameterDirection.Input;

    command.Parameters.Add(p_data, OracleDbType.RefCursor).Direction = 
        ParameterDirection.Output;

    var pflag = command.Parameters.Add(p_flag, OracleDbType.Int32);
    pflag.Direction = ParameterDirection.Output;

    if (command.Connection.State != ConnectionState.Open)
        command.Connection.Open();

    command.Parameters[p_name].Value = name;
    command.Parameters[p_date].Value = date;

    DateTime bdate = DateTime.Now;
    command.ExecuteNonQuery();

    if (((OracleDecimal)command.Parameters[p_flag].Value).ToInt32() == 1)
    {

    }
    else
    {
        using (var oreader = command.ExecuteReader())
        {
            if (oreader != null)
            {
                try
                {
                    while (oreader.Read()){ }
                }
                finally
                {
                    oreader.Close();
                }
            }
        }
    }
    MessageBox.Show(DateTime.Now.Subtract(bdate).ToString());
}

最耗时的代码行似乎是

command.ExecuteNonQuery();

command.ExecuteReader()

光标返回的行数不超过10 - 15,足够少,通过阅读器读取它们需要几毫秒;所以我认为它不是FetchSizeRowSize问题。

在这种情况下,我有什么办法可以提高ODP.Net的性能吗?

2 个答案:

答案 0 :(得分:1)

首先,您的功能包括打开数据库。我想在你的其他工具中,你已经完成了连接,只是执行了命令。根据您的数据库安全性和位置,这需要1-10秒。

我认为这实际上不会让你获得秒数,但你从未使用CommandType.StoredProcedure。相反,你自己构建SQL。让ODP.Net担心这一点。只需将正确的命令类型和过程名称作为文本传递。

答案 1 :(得分:0)

首先确保执行计划实际上是相同的。

与您的DBA合作并要求他们为独立运行(aqua data studio)和您的odp.net呼叫捕获解释计划,并确认它们实际上是相同的。如果他们不是,那么这可能会解释你的问题。然后,您可以尝试将“enlist = false”添加到您的连接字符串,但更好的是让DBA更新相关表的统计信息,希望能够修复慢速计划。有关详细信息,请参阅https://stackoverflow.com/a/14712992/852208

我遇到了同样的问题,而且当涉及分布式事务时,oracle对执行计划不太乐观。

上述答案来自:https://stackoverflow.com/a/15886630/852208。这基本上是“从X运行而不是从Y运行”,所以它可能是重复但我们会看到。