0

我要做的是在许多 Oracle 数据库(至少十几个)上运行相同的 SQL 选择,并在 Gridview 中显示输出。

我已经拼凑了一些可行的东西,但不幸的是它很慢。我认为,十几个数据库中至少有一个总是无法访问或处于错误状态的事实加剧了这种情况。

除了很慢,我不禁认为这不是最好的方法,也不是很像“.NET”。

我在过去写过类似的东西,作为 PHP 中的一个简单循环,它只是依次连接到每个数据库,运行 sql 并写入另一个<tr>,对于给定的查询,它的运行速度至少快两倍。但我对此并不满意,我想提高我的知识!

我正在学习 C# 和 ASP.NET,所以请原谅可怕的代码 :)

public void BindData(string mySQL)
    {
        OracleConnection myConnection;
        OracleDataAdapter TempDataAdapter;
        DataSet MainDataSet = new DataSet();
        DataTable MainDataTable = new DataTable();
        DataSet TempDataSet;
        DataTable TempDataTable;
        string connectionString = "";
        Label1.Visible = false;
        Label1.Text = "";

        foreach (ListItem li in CheckBoxList1.Items)
        {
            if (li.Selected)
            {
                connectionString = "Data Source=" + li.Text + "";
                connectionString += ";Persist Security Info=True;User ID=user;Password=pass;Unicode=True";
                myConnection = new OracleConnection(connectionString);
                try
                {
                    TempDataAdapter = new OracleDataAdapter(mySQL, myConnection);
                    TempDataSet = new DataSet();
                    TempDataTable = new DataTable();
                    TempDataAdapter.Fill(TempDataSet);
                    TempDataTable = TempDataSet.Tables[0].Copy();
                    /* If the main dataset is empty, create a table by cloning from temp dataset, otherwise
                     copy all rows to existing table.*/
                    if (MainDataSet.Tables.Count == 0)
                    {
                        MainDataSet.Tables.Add(TempDataTable);
                        MainDataTable = MainDataSet.Tables[0];
                    }
                    else
                    {
                        foreach (DataRow dr in TempDataTable.Rows)
                        {
                            MainDataTable.ImportRow(dr);
                        }
                    }
                }
                catch (OracleException e)
                {
                    Label1.Visible = true;
                    Label1.Text = Label1.Text + e.Message + " on " + li.Text + "<br>";

                }
                finally
                {
                    if (myConnection != null)
                    {
                        myConnection.Close();
                        myConnection = null;
                    }
                    TempDataSet = null;
                    TempDataAdapter = null;
                    TempDataTable = null;

                }
            }
        }
        GridView1.DataSourceID = String.Empty;
        if (MainDataSet.Tables.Count != 0)
        {
        GridView1.DataSource = MainDataSet;
            if (GridView1.DataSource != null)
            {
                GridView1.DataBind();
            }
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        BindData(TextBox1.Text);
    }

谢谢!

更新:SQL 代码各不相同,为了测试,我使用了非常简单的查询,例如select sysdate from dualor select name from v$database。在最终使用中,它会复杂得多,我的想法是我应该能够运行几乎任何东西,因此BindData(TextBox1.Text)

更新:从 ASP.NET 代码连接到许多数据库而不是一个或所有数据库上的存储过程或复制到一个数据库的原因是双重的。首先,有问题的数据库是几个类似生产环境(通常是开发、测试和支持每个客户端)的频繁更新副本,因此对实际数据库所做的任何事情都必须定期更新或重做,因为它们无论如何都会重新加载。其次,我事先不知道可能会运行什么样的查询,这种形式让我只需select count (name) from dbusers针对十几个数据库键入例如,而无需首先考虑将 dbusers 表复制到主数据库。

4

5 回答 5

3

如果您在 DataTable 对象上运行 DataAdapter.Fill 方法,则该表将使用查询结果进行更新。因此,无需创建新的 DataTable 和 DataSet 对象,然后手动复制 DataRows,您只需将行添加到同一个表中即可。

尝试这样的事情(在未经测试的 C# 代码中):

public void BindData(string mySQL)
{
  OracleConnection myConnection;
  // Empty connection string for now
  OracleDataAdapter MainDataAdapter = new OracleDataAdapter(mySQL, ""); 
  DataTable MainDataTable = new DataTable();
  string connectionString = "";
  Label1.Visible = false;
  Label1.Text = "";

  foreach (ListItem li in CheckBoxList1.Items)
  {
    if (li.Selected)
    {
      connectionString = "Data Source=" + li.Text + "";
      connectionString += ";Persist Security Info=True;User ID=user;Password=pass;Unicode=True";
      MainDataAdapter.SelectCommand.Connection.ConnectionString = connectionString
      try
      {
        MainDataAdapter.Fill(MainDataTable);
      }
      catch (OracleException e)
      {
        Label1.Visible = true;
        Label1.Text = Label1.Text + e.Message + " on " + li.Text + "<br>";
      }
    }
  }
  GridView1.DataSourceID = String.Empty;
  GridView1.DataSource = MainDataTable;
  GridView1.DataBind();
}

我做了以下更改:

  • 创建了一个数据适配器并使用您的 mySQL 查询为其分配了一个选择命令
  • 给连接一个空的连接字符串
  • 创建了一个数据表对象并删除了数据集(只有在查询返回多行时才需要它们)
  • 将循环更改为仅设置 SelectCommand 的连接字符串(您可能必须将其更改为将 SelectCommand 替换为新的)
  • 删除了 connection.Close() 调用。DataAdapter 会自动执行此操作。

就是这样。如果您的数据库处于脱机状态,您仍然会遇到速度变慢的情况,但至少代码更简单、更快,因为您不必在表之间复制所有行。

还有一件事。您可能可以在连接字符串中为连接设置超时。尝试降低这个。

于 2008-11-20T19:58:26.720 回答
2

可能有很多因素导致它变慢。正在执行的运行缓慢的 sql 语句是什么?

如果有人阅读这篇文章正在使用 sql server,Scott Mitchell 刚刚写了一篇很好的文章来帮助在 sql server 中解决这个问题: 对多个数据库运行相同的查询

于 2008-11-20T19:33:42.557 回答
1

为什么不在一个 oracle 数据库上运行单个存储过程,并让 sproc 调用其他数据库?这是使用链接数据库的正确方法。

于 2008-11-20T20:34:52.043 回答
1

Why not use replication to do this...you know, one central database which is pooling for new data from the other databases and just execute your queries over this set of data which is never going to be down.

于 2008-11-20T20:42:53.663 回答
0

听起来您可能对获得这个更通用问题的答案更感兴趣:如何在不挂起 UI(ASP 或 WinForms)的情况下执行长时间运行的任务?

该问题的答案是使用多个Threads。我会在一个单独的线程上执行这样一个长时间运行的任务,并向用户显示一个包含当前结果的页面(自动刷新或使用 ajax 等)。您甚至可以为每个可用的处理器获得幻想并创建任务,以充分利用您的机器(使用类似Parallel Extensions之类的东西);但是,这会显着增加复杂性,并且很难正确处理。

如果您没有在 .Net 中使用过 Threads,可以在这里找到一个很棒的教程(由唯一的Jon Skeet提供)

于 2008-11-20T20:33:16.757 回答