2008-10-08 7 views
2

Question Remplace: Update multiple rows into SQL tablemultiples DB:

Voici un extrait de code pour mettre à jour un résultat d'examen fixés. La structure de DB est comme donnée, mais je peux soumettre des procédures stockées pour l'inclusion (qui sont une douleur à modifier, donc je la sauvegarde jusqu'à la fin.)

La question: Y a-t-il un meilleur moyen d'utiliser SQL Server v 2005. , net 2.0?

string update = @"UPDATE dbo.STUDENTAnswers 
           SET [email protected] 
           WHERE StudentID [email protected] and QuestionNum [email protected]"; 
      SqlCommand updateCommand = new SqlCommand(update, conn); 
      conn.Open(); 

      string uid = Session["uid"].ToString(); 
      for (int i= tempStart; i <= tempEnd; i++) 
      { 
       updateCommand.Parameters.Clear(); 
       updateCommand.Parameters.AddWithValue("@ID",uid); 
       updateCommand.Parameters.AddWithValue("@qnum",i); 
       updateCommand.Parameters.AddWithValue("@answer", Request.Form[i.ToString()]); 
       try 
       { 
        updateCommand.ExecuteNonQuery(); 
       } 
       catch { } 
      } 

Répondre

4

Quelques choses se distinguent:

  • Vous ne montrent pas où l'SqlConnection est instancié, il est donc pas évident que vous disposer correctement.

  • Vous ne devez pas avaler des exceptions dans la boucle - mieux vaut les gérer dans un gestionnaire d'exceptions de niveau supérieur.

  • Vous instanciez de nouveaux paramètres à chaque itération dans la boucle - vous pourriez simplement réutiliser les paramètres.

Mettre cela ensemble, il pourrait ressembler à quelque chose comme ce qui suit (si vous ne voulez pas utiliser une transaction, c.-à-ne se soucient pas si certains, mais pas toutes les mises à jour réussir):

using (SqlConnection conn = new SqlConnection(connectionString)) 
{ 
    conn.Open(); 
    using (SqlCommand updateCommand = new SqlCommand(update, conn)) 
    { 
     string uid = Session["uid"].ToString(); 
     updateCommand.Parameters.AddWithValue("@ID", uid); 
     updateCommand.Parameters.AddWithValue("@qnum", i); 
     updateCommand.Parameters.Add("@answer", System.Data.SqlDbType.VarChar); 
     for (int i = tempStart; i <= tempEnd; i++) 
     { 
      updateCommand.Parameters["@answer"] = Request.Form[i.ToString()]; 
      updateCommand.ExecuteNonQuery(); 
     } 
    } 
} 

ou d'utiliser une transaction pour assurer tout ou rien:

using (SqlConnection conn = new SqlConnection(connectionString)) 
{ 
    conn.Open(); 
    using (SqlTransaction transaction = conn.BeginTransaction()) 
    { 
     using (SqlCommand updateCommand = new SqlCommand(update, conn, transaction)) 
     { 
      string uid = Session["uid"].ToString(); 
      updateCommand.Parameters.AddWithValue("@ID", uid); 
      updateCommand.Parameters.AddWithValue("@qnum", i); 
      updateCommand.Parameters.Add("@answer", System.Data.SqlDbType.VarChar); 
      for (int i = tempStart; i <= tempEnd; i++) 
      { 
       updateCommand.Parameters["@answer"] = Request.Form[i.ToString()]; 
       updateCommand.ExecuteNonQuery(); 
      } 
      transaction.Commit(); 
     } 
    } // Transaction will be disposed and rolled back here if an exception is thrown 
} 

Enfin, un autre problème est que vous mélangez le code de l'interface utilisateur (par exemple Request.Form) avec le code d'accès aux données. Il serait plus modulaire et testable de séparer ces - par exemple. en divisant votre application en couches UI, Business Logic et Data Access.

+0

Je pense que la gestion des exceptions dans la boucle ou non devrait dépendre des règles métier. Traiter des exceptions en dehors de la boucle signifie que vous arrêtez le traitement de l'élément avec l'erreur ou annulez la transaction, mais il peut arriver que vous souhaitiez ignorer la ligne d'erreur et poursuivre le traitement. – palmsey

+0

- Le code de journalisation a été supprimé. –

+0

Si vous souhaitez ignorer une ligne d'erreur et continuer le traitement d'autres lignes, vous souhaiterez probablement récupérer SqlException et inspecter la propriété SqlErrors pour rechercher des erreurs spécifiques. – Joe

-1

émettent une mise à jour unique qui va à l'encontre d'une table de valeurs:

UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN (
    SELECT 1 as q, 'answer1' as a 
    UNION ALL SELECT 2, 'answer2' -- etc... 
) x ON s.QuestionNum=x.q AND [email protected] 

si vous venez de mettre cela ensemble comme ceci:

using(SqlCommand updateCommand = new SqlCommand()) { 
    updateCommand.CommandType = CommandType.Text; 
    updateCommand.Connection = conn; 
    if (cn.State != ConnectionState.Open) conn.Open(); 

    StringBuilder sb = new StringBuilder("UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN ("); 
    string fmt = "SELECT {0} as q, @A{0} as a"; 
    for(int i=tempStart; i<tempEnd; i++) { 
    sb.AppendFormat(fmt, i); 
    fmt=" UNION ALL SELECT {0},@A{0}"; 
    updateCommand.Parameters.AddWithValue("@A"+i.ToString(), Request.Form[i.ToString()]); 
    } 
    sb.Append(") x ON s.QuestionNum=x.q AND [email protected]"); 
    updateCommand.CommandText = sb.ToString(); 
    updateCommand.Parameters.AddWithValue("@ID", uid); 
    updateCommand.ExecuteNonQuery(); 
} 

Cela présente les avantages d'être un tout autre rien d'opération (comme si vous aviez enveloppé plusieurs mises à jour dans une transaction) et courra plus vite depuis:

  • La table et les index associés sont examinés/mis à jour une fois
  • Vous ne payez que pour le temps d'attente entre votre application et le serveur de base de données une fois, plutôt que sur chaque mise à jour
0

Un problème que je vois est quand vous ouvrez votre connexion.

Je voudrais au moins avant chaque mise à jour appeler l'ouvrir puis fermez la connexion après la mise à jour.

Si votre boucle prend du temps à s'exécuter, votre connexion restera ouverte pendant une longue période.

C'est une bonne règle de ne jamais ouvrir votre commande tant que vous n'en avez pas besoin.

+0

La boucle s'exécute environ 30 fois par publication. Ma pensée était que je ferais des économies d'ouverture et de fermeture 30 fois. Est-ce que je me trompe ici? –

+0

Je suis d'accord, je préfère avoir une connexion ouverte pendant que la boucle doit être exécutée que d'ouvrir et de fermer la connexion N fois. – palmsey

0

Vous pouvez insérer en masse en utilisant OpenXML. Créez un document XML contenant toutes vos questions et réponses et utilisez-le pour insérer les valeurs.

Edit: Si vous restez avec votre solution actuelle, je serais au moins envelopper votre SqlConnection et SqlCommand dans un bloc à l'aide pour vous assurer qu'ils obtiennent disposés.

2

Pour 30 mises à jour, je pense que vous êtes sur la bonne voie, bien que le commentaire sur la nécessité d'une utilisation autour de updateCommand est correct.

Nous avons trouvé la façon la plus efficace de faire des mises à jour groupées (> 100 lignes) via la classe SqlBulkCopy à une table temporaire suivie d'un appel de procédure stockée pour remplir la table dynamique.