为什么 OracleDataAdapter.Fill() 非常慢?

发布于 2024-08-24 18:16:00 字数 2216 浏览 12 评论 0原文

我正在使用一个非常复杂的查询从我们的计费数据库之一检索一些数据。

我遇到了一个问题,即使用 SQL Developer 执行查询时似乎可以相当快地完成,但使用 OracleDataAdapter.Fill() 方法时似乎永远不会完成。

我只尝试读取大约 1000 行,查询在 SQL Developer 中大约 20 秒内完成。

是什么导致性能如此巨大的差异?我有大量其他查询可以使用相同的函数快速运行。


下面是我用来执行查询的代码:

using Oracle.DataAccess.Client;

...

public DataTable ExecuteExternalQuery(string connectionString, string providerName, string queryText)
{
    DbConnection connection = null;
    DbCommand selectCommand = null;
    DbDataAdapter adapter = null;

    switch (providerName)
    {
        case "System.Data.OracleClient":
        case "Oracle.DataAccess.Client":
            connection = new OracleConnection(connectionString);
            selectCommand = connection.CreateCommand();
            adapter = new OracleDataAdapter((OracleCommand)selectCommand);
            break;
        ...
    }

    DataTable table = null;
    try
    {
        connection.Open();

        selectCommand.CommandText = queryText;
        selectCommand.CommandTimeout = 300000;
        selectCommand.CommandType = CommandType.Text;

        table = new DataTable("result");
        table.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(table);
    }
    finally
    {
        adapter.Dispose();

        if (connection.State != ConnectionState.Closed)
        {
            connection.Close();
        }
    }

    return table;
}

下面是我正在使用的 SQL 的概要:

with
  trouble_calls as
  (
    select
      work_order_number,
      account_number,
      date_entered
    from
      work_orders
    where
      date_entered >= sysdate - (15 + 31)  -- Use the index to limit the number of rows scanned
     and
      wo_status not in ('Cancelled')
     and
      wo_type = 'Trouble Call'
  )
select
  account_number,
  work_order_number,
  date_entered
from
  trouble_calls wo
where
  wo.icoms_date >= sysdate - 15
 and
  (
    select
      count(*)
    from
      trouble_calls repeat
    where
      wo.account_number = repeat.account_number
     and
      wo.work_order_number <> repeat.work_order_number
     and
      wo.date_entered - repeat.date_entered between 0 and 30
  ) >= 1

I am using a pretty complex query to retrieve some data out of one of our billing databases.

I'm running in to an issue where the query seems to complete fairly quickly when executed with SQL Developer, but does not seem to ever finish when using the OracleDataAdapter.Fill() method.

I'm only trying to read about 1000 rows, and the query completes in SQL Developer in about 20 seconds.

What could be causing such drastic differences in performance? I have tons of other queries that run quickly using the same function.


Here is the code I'm using to execute the query:

using Oracle.DataAccess.Client;

...

public DataTable ExecuteExternalQuery(string connectionString, string providerName, string queryText)
{
    DbConnection connection = null;
    DbCommand selectCommand = null;
    DbDataAdapter adapter = null;

    switch (providerName)
    {
        case "System.Data.OracleClient":
        case "Oracle.DataAccess.Client":
            connection = new OracleConnection(connectionString);
            selectCommand = connection.CreateCommand();
            adapter = new OracleDataAdapter((OracleCommand)selectCommand);
            break;
        ...
    }

    DataTable table = null;
    try
    {
        connection.Open();

        selectCommand.CommandText = queryText;
        selectCommand.CommandTimeout = 300000;
        selectCommand.CommandType = CommandType.Text;

        table = new DataTable("result");
        table.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(table);
    }
    finally
    {
        adapter.Dispose();

        if (connection.State != ConnectionState.Closed)
        {
            connection.Close();
        }
    }

    return table;
}

And here is the general outline of the SQL I'm using:

with
  trouble_calls as
  (
    select
      work_order_number,
      account_number,
      date_entered
    from
      work_orders
    where
      date_entered >= sysdate - (15 + 31)  -- Use the index to limit the number of rows scanned
     and
      wo_status not in ('Cancelled')
     and
      wo_type = 'Trouble Call'
  )
select
  account_number,
  work_order_number,
  date_entered
from
  trouble_calls wo
where
  wo.icoms_date >= sysdate - 15
 and
  (
    select
      count(*)
    from
      trouble_calls repeat
    where
      wo.account_number = repeat.account_number
     and
      wo.work_order_number <> repeat.work_order_number
     and
      wo.date_entered - repeat.date_entered between 0 and 30
  ) >= 1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倒数 2024-08-31 18:16:00

这段代码帮助了我,尝试一下:

using (OracleConnection conn = new OracleConnection())
{
     OracleCommand comm = new OracleCommand();
     comm.Connection = conn;
     comm.FetchSize = comm.FetchSize * 16;
     comm.CommandText = "select * from some_table";

     try
     {
          conn.Open();
          OracleDataAdapter adap = new OracleDataAdapter(comm);
          System.Data.DataTable dt = new System.Data.DataTable();
          adap.Fill(dt);
     }
     finally
     {
          conn.Close();
     }
}

trik is in line(尝试从 8 到 64 的值以找到最适合您的情况):

comm.FetchSize = comm.FetchSize * 16;

更新:

这是一个改进的代码:

OracleConnection myConnection = new OracleConnection(myConnectionString);
OracleCommand myCommand = new OracleCommand(mySelectQuery, myConnection);
myConnection.Open();
using (OracleDataReader reader = myCommand.ExecuteReader(CommandBehavior.CloseConnection))
{
    // here goes the trick
    // lets get 1000 rows on each round trip
    reader.FetchSize = reader.RowSize * 1000;

    while (reader.Read())
    {
        // reads the records normally
    }
}// close and dispose stuff here

来自 此处

This code helped me, try it:

using (OracleConnection conn = new OracleConnection())
{
     OracleCommand comm = new OracleCommand();
     comm.Connection = conn;
     comm.FetchSize = comm.FetchSize * 16;
     comm.CommandText = "select * from some_table";

     try
     {
          conn.Open();
          OracleDataAdapter adap = new OracleDataAdapter(comm);
          System.Data.DataTable dt = new System.Data.DataTable();
          adap.Fill(dt);
     }
     finally
     {
          conn.Close();
     }
}

The trik is in line (try values from 8 to 64 to find the best for your case):

comm.FetchSize = comm.FetchSize * 16;

UPDATE:

Here is an improved code:

OracleConnection myConnection = new OracleConnection(myConnectionString);
OracleCommand myCommand = new OracleCommand(mySelectQuery, myConnection);
myConnection.Open();
using (OracleDataReader reader = myCommand.ExecuteReader(CommandBehavior.CloseConnection))
{
    // here goes the trick
    // lets get 1000 rows on each round trip
    reader.FetchSize = reader.RowSize * 1000;

    while (reader.Read())
    {
        // reads the records normally
    }
}// close and dispose stuff here

From here

从此见与不见 2024-08-31 18:16:00

使用 Microsoft Data Provider for Oracle 和本机 Oracle Data Provider 之间存在已知的性能差异。

你两种都尝试过吗?

您想通过此查询实现什么目的?忘记技术性的东西,只是这一切的目标。也许有适合您查询的曲调。

您是否尝试过使用探查器来查看它卡在哪里?

There are known performance differences between using the Microsoft Data Provider for Oracle and the native Oracle Data Provider.

Have you tried both?

What are you trying to achieve with this query? Forget about technical stuff, just the objective of it all. Perhaps is there a tune possible for your query.

Have you tried with a profiler to see where it gets stuck?

失眠症患者 2024-08-31 18:16:00

我认为 Oracle 查询返回的区域性和日期是不同的,这就是应用程序需要花费大量时间来解析的地方。

I think the culture and the date being returned by your Oracle query are different and that is where the application is taking a lot of time to parse.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文