2011-08-15 2 views
2

J'ai besoin de remplir deux listes déroulantes avec une liste de bases de données attachées à un serveur qui contient une table spécifiquement nommée. Ma méthode actuelle est celle-ci.Méthode efficace pour obtenir une liste de bases de données contenant une table

List<string> dbType1 = new List<string>(); 
List<string> dbType2 = new List<string>(); 
using (var conn = new SqlConnection("Data Source=(local);Integrated Security=true")) 
using (var cmd = new SqlCommand()) 
{ 
    conn.Open(); 
    cmd.Connection = conn; 

    cmd.CommandText = "select name from sys.databases"; 
    using (var innerConn = new SqlConnection("Data Source=(local);Integrated Security=true")) 
    using (var innerCmd = new SqlCommand()) 
    using (var rdr = cmd.ExecuteReader()) 
    { 
     innerConn.Open(); 
     innerCmd.Connection = innerConn; 

     while (rdr.Read()) 
     { 
      string table = rdr.GetString(0); 
      innerCmd.CommandText = String.Format("select name from [{0}]..sys.tables where name in 'EF_LAB_FIELDS_DYNA' 'AUTOXPAY_PAYMENTS'", table); 

      object result = innerCmd.ExecuteScalar(); 

      if(result != null) 
      { 
       if ((string)result == "EF_LAB_FIELDS_DYNA") 
        dbType1.Add(table); 
       else 
        dbType2.Add(table); 
      } 
     } 

    } 

} 

cb.Items.AddRange(dbType1.ToArray()); 
cb2.Items.AddRange(dbType2.ToArray()); 

Cela fonctionne, mais sur un serveur avec 205 bases de données attachées, l'exécution prend 44,6 secondes.

Quelqu'un peut-il me donner des conseils sur la façon d'accélérer cette opération? Je suis ouvert à l'utilisation d'autres techniques comme SMO, en faisant plus de traitement côté client, ou en exécutant cela comme une forme de requête compliquée sur le serveur. Tant que j'obtiens les deux listes basées sur les noms des deux tables, cela répond à mes besoins.


Voici la version mise à jour qui a ramené mon temps d'exécution à moins d'une seconde grâce à la suggestion de total.

ConcurrentBag<string> dbType1 = new ConcurrentBag<string>(); 
ConcurrentBag<string> dbType2 = new ConcurrentBag<string>(); 
List<string> databases = new List<string>(); 
using (var conn = new SqlConnection("Data Source=(local);Integrated Security=true")) 
using (var cmd = new SqlCommand()) 
{ 
    conn.Open(); 
    cmd.Connection = conn; 

    cmd.CommandText = "select name from sys.databases"; 

    using (var rdr = cmd.ExecuteReader()) 
    { 
     while (rdr.Read()) 
     { 
      databases.Add(rdr.GetString(0)); 
     } 

     Parallel.ForEach(databases,() => 
      { 
       var innerConn = new SqlConnection("Data Source=(local);Integrated Security=true"); 
       var innerCmd = new SqlCommand("", innerConn); 
       innerConn.Open(); 
       return innerCmd; 
      }, 
      (database, loopState, localCommand) => 
      { 
       localCommand.CommandText = String.Format("select name from [{0}].sys.tables where name in ('EF_LAB_FIELDS_DYNA', 'AUTOXPAY_PAYMENTS')", database); 

       object result = localCommand.ExecuteScalar(); 

       if (result != null) 
       { 
        if ((string)result == "EF_LAB_FIELDS_DYNA") 
         dbType1.Add(database); 
        else 
         dbType2.Add(database); 
       } 

       return localCommand; 
      }, 
      (localCommand) => 
      { 
       var temp = localCommand.Connection; 
       localCommand.Dispose(); 
       temp.Dispose(); 
      }); 
    } 

} 
+0

Avez-vous profilé pour voir où l'heure est prise? ma conjecture ouvre principalement la connexion (s) que vous ne pouvez probablement pas éviter. – pstanton

+0

@pstanton Je n'ouvre que deux connexions et les réutilise, le temps est passé à attendre sur la requête si la table existe. –

Répondre

4

Je voudrais exécuter chacun d'eux dans un thread séparé, puis les joindre tous avant de continuer. Pour ce faire, vous devrez utiliser des structures thread-safe, mais cela vous permettra d'envoyer toutes vos demandes en même temps pour que vous n'ayez pas à attendre que chacune passe à la suivante. Vous pourriez vouloir utiliser un pool de threads au lieu d'un thread pour chacun si vous avez 200 bases de données quelque chose, mais encore une fois, 200 threads n'est pas beaucoup ... Expérience et profil, mais c'est l'approche générale que je voudrais prendre.

+0

assurez-vous également de partager la connexion entre eux si possible –

+0

Mettre cette requête dans un 'Parallel.ForEach' m'a ramené à 0 .8 secondes d'exécution. –

+0

Excellent. Je ne suis pas un gars C# donc je ne pourrais pas vous avoir donné de détails. Heureux la solution générale pourrait être appliquée avec votre connaissance de la bibliothèque pour le succès! 8 est beaucoup mieux que 44! :-RÉ – corsiKa

Questions connexes