2010-03-31 12 views
2

Je charge un paquet de lignes dans MySQL en C#. Dans MS Sql, je peux alimenter un DataReader en SqlBulkCopy, mais MySqlBulkCopy ne se présente que comme un bootstrap pour un chargement en masse à partir d'un fichier. Donc, ma solution actuelle utilise une commande préparée dans une boucle transactionnelle.MySql BulkCopy/Insert from DataReader

Existe-t-il un moyen plus rapide pour effectuer le chargement en bloc de MySQL à l'aide d'une source DataReader?

Voici le code.

public override void WriteToServer(IDataReader reader) 
    { 
     const string insertFormat = "insert into `{3}`.`{0}` ({1}) values ({2});"; 

     string names = string.Join(",", 
           _command.Parameters.Cast<MySqlParameter>().Select(p => p.ParameterName).ToArray()); 
     string vals = string.Join(",", 
           _command.Parameters.Cast<MySqlParameter>().Select(p => "?" + p.ParameterName). 
            ToArray()); 

     _command.CommandText = string.Format(insertFormat, _table, names, vals, _schema); 

     int reportCounter = 0; 
     int totalRecords = 0; 
     bool finished = false; 

     using (var connection = new MySqlConnection(_source)) 
     { 
      connection.Open(); 
      _command.Connection = connection; 
      _command.Prepare(); 

      while (!finished) 
      { 
       using (MySqlTransaction dbTrans = connection.BeginTransaction(IsolationLevel.ReadUncommitted)) 
       { 
        for (int i = 0; i < BatchSize; i++) 
        { 
         if (!reader.Read()) 
         { 
          finished = true; 
          break; 
         } 

         try 
         { 
          for (int p = 0; p < _command.Parameters.Count; p++) 
          { 
           _command.Parameters[p].Value = reader.GetValue(p); 
          } 
          _command.ExecuteNonQuery(); 
         } 
         catch (Exception ex) 
         { 
          Trace.WriteLine(ex.Message); 
         } 
         reportCounter++; 
         totalRecords++; 

         if (reportCounter >= NotifyAfter) 
         { 
          reportCounter = 0; 
          OnSqlRowsCopied(new SqlRowsCopiedEventArgs(totalRecords)); 
         } 
        } 
        dbTrans.Commit(); 
       } 
      } 
     } 
    } 

Répondre

3

Mis à part en utilisant le mécanisme "LOAD DATA dans le fichier" MySQL a un 'insert en vrac' standard non-SQL où vous pouvez spécifier plusieurs 'valeurs' à insérer: http://dev.mysql.com/doc/refman/5.0/en/insert.html

INSERT INTO TABLE x (a,b,c,e,d,f,g,...) 
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ...) 
    , (?, ?, ?, ?, ?, ?, ?, ?, ...) 
    , (?, ?, ?, ?, ?, ?, ?, ?, ...) 
    , (?, ?, ?, ?, ?, ?, ?, ?, ...) 
    , (?, ?, ?, ?, ?, ?, ?, ?, ...) 

Cet exemple insérera un «bloc» de 5 lignes. Vous pouvez utiliser des instructions "préparées" pour améliorer les performances par rapport à sql généré par ligne. L'inconvénient de ceci est quand, après avoir chargé des millions d'enregistrements, il ne vous reste plus que 3 lignes à insérer. Vous aurez besoin de re-préparer votre SQL avec un insert à 3 lignes. Ne soyez pas tenté d'utiliser des valeurs nulles pour les 4ème et 5ème enregistrements manquants sauf si vous utilisez 'INSERT IGNORE', mais c'est plus lent qu'un insert typique. La re-préparation est très rapide et vaut les résultats.

Nous avons une table où la taille d'un bloc d'insertion est 200+ rangées! La quantité maximale de lignes par insertion dépend de la taille de la mémoire que votre système d'exploitation considère comme le point de basculement entre mmap() et malloc(). Pour Solaris 10, nous utilisons "4096/rows_size = rows_per_insert". Il y a un bug mysql quelque part sur ce problème qui est vaguement lié à read_buffer_size.

+1

D'accord, et j'ai donné un coup de feu et a rencontré un problème de taille de paquet. Je crée la commande db via et n'ai pas trouvé d'instruction DDL pour définir la taille maximale des paquets. Donc, en tirant parti de votre expérience, je dois d'une manière ou d'une autre déterminer par programmation le point bascule entre mmap() et malloc(), définir ma taille de paquet juste en dessous, et préparer la commande d'insertion principale en conséquence. Vous avez raison? Un autre problème qui peut réduire l'efficacité est que les données source ont de grands champs de texte. –