Pergunta

O que estou tentando fazer é executar o mesmo SQL escolha de muitos bancos de dados Oracle (pelo menos uma dúzia), e exibir a saída em um GridView.

Eu tenho cortado em conjunto algo que funciona, mas infelizmente é muito lento. Eu acho que é agravada pelo fato de que pelo menos 1 dos dúzia de bancos de dados será invariavelmente inacessível ou não em estado de erro.

Além de ser lenta não posso deixar de pensar que não é a melhor maneira de fazê-lo, nem muito 'NET' like.

Eu escrevi algo semelhante no passado como um loop simples em PHP que apenas se conecta a cada db por sua vez, corre o sql e escreve outra <tr>, e ele funciona, pelo menos, duas vezes mais rápido, para uma determinada consulta. Mas eu não estou realmente feliz com isso, eu gostaria de melhorar o meu conhecimento!

Estou aprendendo C # e ASP.NET por isso, desculpa o código horrível:)

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

Obrigado!

UPDATE: O código SQL varia, para testar eu usei consultas muito simples, como select sysdate from dual ou select name from v$database. Em uso eventual, será muito mais complicado, a idéia é que eu deveria ser capaz de executar praticamente qualquer coisa, daí a BindData(TextBox1.Text)

UPDATE: A razão para se conectar a vários bancos de dados a partir do código ASP.NET em vez de um procedimento armazenado em um ou todos os dbs, ou replicar a um db, é duplo. Em primeiro lugar, os dbs em questão são atualizadas frequentemente réplicas de vários ambientes de produção semelhantes (tipicamente desenvolvimento, teste e suporte para cada cliente), então qualquer coisa feito para os dbs reais teriam de ser atualizados ou refeito regularmente como eles são recarregados de qualquer maneira. Em segundo lugar, eu não sei de antemão que tipo de consulta pode ser executado, esta forma permite que me basta digitar por exemplo select count (name) from dbusers contra uma dúzia de bancos de dados sem ter que primeiro pensar em replicar a tabela dbusers a um db mestre.

Foi útil?

Solução

Se você executar o método DataAdapter.Fill em um objeto DataTable a tabela será atualizada com os resultados da consulta. Então, ao invés de criar um novo DataTable e DataSet objetos e, em seguida, copiar os DataRows manualmente, você pode simplesmente adicionar linhas à mesma mesa.

Tente algo assim (em código não testado 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();
}

Eu fiz as seguintes alterações:

  • Criado um adaptador de dados e atribuiu-lhe um comando select usando seu MySQL Query
  • Deu a conexão de uma seqüência de conexão vazio
  • Criado um objeto tabela de dados e removido os conjuntos de dados (você só precisa-los se os seus consulta retorna várias linhas)
  • Mudou você loop para apenas definir a seqüência de conexão do SelectCommand (você pode ter que mudar isso para substituir o SelectCommand com um novo)
  • Removido as chamadas Connection.Close (). O DataAdapter faz isso automaticamente.

e é isso. Se os bancos de dados estão offline você ainda vai experimentar lentidão, mas pelo menos o código é mais simples e mais rápido desde que você não tem que copiar todas as linhas entre suas tabelas.

Só mais uma coisa. Provavelmente, você pode definir um tempo limite para a conexão na seqüência de conexão. Tentar diminuir este.

Outras dicas

poderia ser um monte de fatores que o causam a ser lento. O que está a instrução SQL que está sendo executado que está correndo lento?

Se alguém ler este está usando o sql server, Scott Mitchell escreveu um belo artigo para ajudar a resolver isso em sql server: executar a mesma consulta em relação a vários bancos de dados

Por que não usar a replicação para fazer isso ... você sabe, um banco de dados central que é pooling de novos dados de outros bancos de dados e apenas executar suas consultas sobre este conjunto de dados que é não indo a ser baixo.

Por que não executar um único procedimento armazenado em um banco de dados Oracle, e têm a chamada sproc os outros bancos de dados? Esta é a maneira correta de trabalhar com bancos de dados vinculados.

Parece que você pode estar mais interessado em obter uma resposta a esta questão mais genérica:? Como posso executar uma tarefa de longa duração sem desligar a interface do usuário (ASP ou WinForms)

A resposta a essa pergunta é usar múltiplos Threads . Gostaria de executar uma tarefa de longa duração como este em um segmento separado e mostrar ao usuário uma página com os resultados atuais (quer refrescantes automaticamente ou com ajax, etc). Você pode até mesmo começar a fantasia e criar tarefas para cada processador disponível para tirar o máximo proveito da sua máquina (usando algo parecido com o Extensões paralelas ); no entanto, isso aumenta a complexidade significativamente e pode ser difícil de acertar.

Se você não tenha trabalhado com linhas em .Net um ótimo tutorial pode ser encontrada aqui (pela primeira e única Jon Skeet )

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top