Je dois insérer environ 2 millions de lignes d'un fichier texte.
Et avec l'insertion, je dois créer des tables maîtres.
Quel est le moyen le plus simple et le plus rapide d’insérer un aussi vaste ensemble de données dans SQL Server?
Vous pouvez essayer avec SqlBulkCopy
class.
Vous permet de charger efficacement en vrac une table SQL Server avec les données de une autre source.
Il y a un article de blog blog sur la façon de l'utiliser.
Je pense qu'il vaut mieux que vous lisiez les données d'un fichier texte dans DataSet
Essayez SqlBulkCopy - Insertion en bloc dans SQL à partir de l'application C #
// connect to SQL
using (SqlConnection connection =
new SqlConnection(connString))
{
// make sure to enable triggers
// more on triggers in next post
SqlBulkCopy bulkCopy =
new SqlBulkCopy
(
connection,
SqlBulkCopyOptions.TableLock |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.UseInternalTransaction,
null
);
// set the destination table name
bulkCopy.DestinationTableName = this.tableName;
connection.Open();
// write the data in the "dataTable"
bulkCopy.WriteToServer(dataTable);
connection.Close();
}
// reset
this.dataTable.Clear();
ou
après avoir fait l'étape 1 en haut
vous pouvez consulter cet article pour plus de détails: Insertion en bloc de données à l'aide de C # DataTable et de la fonction OpenXML du serveur SQL
Mais ce n’est pas testé avec 2 millions d’enregistrements, mais consomme de la mémoire sur la machine car vous devez charger 2 millions d’enregistrements et les insérer.
Re la solution pour SqlBulkCopy:
J'ai utilisé le StreamReader pour convertir et traiter le fichier texte. Le résultat était une liste de mon objet.
J'ai créé une classe qui prend Datatable
ou un List<T>
et une taille de tampon (CommitBatchSize
). Il convertira la liste en une table de données en utilisant une extension (dans la deuxième classe).
Ça marche très vite. Sur mon PC, je peux insérer plus de 10 millions d'enregistrements compliqués en moins de 10 secondes.
Voici la classe:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAL
{
public class BulkUploadToSql<T>
{
public IList<T> InternalStore { get; set; }
public string TableName { get; set; }
public int CommitBatchSize { get; set; }=1000;
public string ConnectionString { get; set; }
public void Commit()
{
if (InternalStore.Count>0)
{
DataTable dt;
int numberOfPages = (InternalStore.Count / CommitBatchSize) + (InternalStore.Count % CommitBatchSize == 0 ? 0 : 1);
for (int pageIndex = 0; pageIndex < numberOfPages; pageIndex++)
{
dt= InternalStore.Skip(pageIndex * CommitBatchSize).Take(CommitBatchSize).ToDataTable();
BulkInsert(dt);
}
}
}
public void BulkInsert(DataTable dt)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
// make sure to enable triggers
// more on triggers in next post
SqlBulkCopy bulkCopy =
new SqlBulkCopy
(
connection,
SqlBulkCopyOptions.TableLock |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.UseInternalTransaction,
null
);
// set the destination table name
bulkCopy.DestinationTableName = TableName;
connection.Open();
// write the data in the "dataTable"
bulkCopy.WriteToServer(dt);
connection.Close();
}
// reset
//this.dataTable.Clear();
}
}
public static class BulkUploadToSqlHelper
{
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
}
}
Voici un exemple lorsque je veux insérer une liste de mon objet personnalisé List<PuckDetection>
(ListDetections
):
var objBulk = new BulkUploadToSql<PuckDetection>()
{
InternalStore = ListDetections,
TableName= "PuckDetections",
CommitBatchSize=1000,
ConnectionString="ENTER YOU CONNECTION STRING"
};
objBulk.Commit();
La classe BulkInsert
peut être modifiée pour ajouter un mappage de colonne si nécessaire. Exemple, vous avez une clé d’identité en tant que première colonne (en supposant que les noms de colonne du datatable soient les mêmes que ceux de la base de données).
//ADD COLUMN MAPPING
foreach (DataColumn col in dt.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
Je me suis récemment heurté à ce scénario (bien plus de 7 millions de lignes) et à l'utilisation de sqlcmd via powershell (après l'analyse de données brutes dans des instructions d'insertion SQL) par segments de 5 000 éléments à la fois (SQL ne peut pas gérer 7 millions de lignes en une seule tâche ou même 500 000 lignes, sauf si elles sont décomposées en éléments plus petits de 5 Ko. Vous pouvez ensuite exécuter chaque script 5K l'un après l'autre.) car je devais tirer parti de la nouvelle commande de séquence dans SQL Server 2012 Enterprise. Je ne pouvais pas trouver de méthode de programmation pour insérer sept millions de lignes de données rapidement et efficacement avec ladite commande de séquence.
Deuxièmement, l'une des choses à surveiller lors de l'insertion d'un million de lignes ou plus de données en une seule séance est la consommation de CPU et de mémoire (principalement de la mémoire) pendant le processus d'insertion. SQL consommera de la mémoire/du processeur avec un travail de cette ampleur sans libérer lesdits processus. Inutile de dire que si votre serveur ne dispose pas de suffisamment de puissance de traitement ou de mémoire, vous pouvez le faire planter assez facilement en peu de temps (ce que j’ai découvert à la dure). Si vous arrivez au point où votre consommation de mémoire est supérieure à 70-75%, redémarrez simplement le serveur et les processus seront remis à l'état normal.
Avant de pouvoir avoir un plan d'exécution final, je devais exécuter de nombreux essais et tests d'erreurs pour connaître les limites de mon serveur (compte tenu des ressources CPU/mémoire limitées avec lesquelles travailler). Je vous suggérerais de faire la même chose dans un environnement de test avant de le mettre en production.
J'utilise l'utilitaire bcp. (Programme de copie en bloc) Je charge environ 1,5 million d’enregistrements de texte chaque mois . Chaque enregistrement de texte mesure 800 caractères de large . Table SQL Server.
Les instructions pour bcp sont à http://msdn.Microsoft.com/en-us/library/ms162802.aspx
J'ai essayé avec cette méthode et cela a considérablement réduit le temps d'exécution de l'insertion de ma base de données.
List<string> toinsert = new List<string>();
StringBuilder insertCmd = new StringBuilder("INSERT INTO tabblename (col1, col2, col3) VALUES ");
foreach (traverse your loop here)
{
toinsert.Add(string.Format("( '{0}', '{1}', '{2}' )", "Val1", "Val2", "Val3"));
}
if (toinsert.Count != 0)
{
insertCmd.Append(string.Join(",", toinsert));
insertCmd.Append(";");
}
using (MySqlCommand myCmd = new MySqlCommand(insertCmd.ToString(), SQLconnectionObject))
{
myCmd.CommandType = CommandType.Text;
myCmd.ExecuteNonQuery();
}
* Créez un objet de connexion SQL et remplacez-le là où j'ai écrit SQLconnectionObject.