web-dev-qa-db-fra.com

Insérer un DataTable entier dans la base de données à la fois au lieu de ligne par ligne?

J'ai un DataTable et j'ai besoin que tout soit poussé vers une table de base de données.

Je peux tout mettre là-dedans avec un foreach et insérer chaque ligne à la fois. Cela va très lentement car il y a quelques milliers de lignes.

Existe-t-il un moyen de faire la totalité de la table de données à la fois qui pourrait être plus rapide?

Le DataTable a moins de colonnes que la table SQL. les autres doivent être laissés NULL.

25
Kyle

J'ai découvert que SqlBulkCopy est un moyen simple de le faire et ne nécessite pas d'écriture d'une procédure stockée dans SQL Server.

Voici un exemple de la façon dont je l'ai implémenté:

// take note of SqlBulkCopyOptions.KeepIdentity , you may or may not want to use this for your situation.  

using (var bulkCopy = new SqlBulkCopy(_connection.ConnectionString, SqlBulkCopyOptions.KeepIdentity))
{
      // my DataTable column names match my SQL Column names, so I simply made this loop. However if your column names don't match, just pass in which datatable name matches the SQL column name in Column Mappings
      foreach (DataColumn col in table.Columns)
      {
          bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
      }

      bulkCopy.BulkCopyTimeout = 600;
      bulkCopy.DestinationTableName = destinationTableName;
      bulkCopy.WriteToServer(table);
}
58
Kyle

Étant donné que vous disposez déjà d'un DataTable et que je suppose que vous utilisez SQL Server 2008 ou mieux, c'est probablement le moyen le plus simple. Tout d'abord, dans votre base de données, créez les deux objets suivants:

CREATE TYPE dbo.MyDataTable -- you can be more speciifc here
AS TABLE
(
  col1 INT,
  col2 DATETIME
  -- etc etc. The columns you have in your data table.
);
GO

CREATE PROCEDURE dbo.InsertMyDataTable
  @dt AS dbo.MyDataTable READONLY
AS
BEGIN
  SET NOCOUNT ON;

  INSERT dbo.RealTable(column list) SELECT column list FROM @dt;
END
GO

Maintenant dans votre code C #:

DataTable tvp = new DataTable();
// define / populate DataTable

using (connectionObject)
{
    SqlCommand cmd = new SqlCommand("dbo.InsertMyDataTable", connectionObject);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@dt", tvp);
    tvparam.SqlDbType = SqlDbType.Structured;
    cmd.ExecuteNonQuery();
}

Si vous aviez donné des détails plus précis dans votre question, j'aurais donné une réponse plus précise.

31
Aaron Bertrand

Considérez cette approche , vous n'avez pas besoin d'une boucle for:

using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
    bulkCopy.DestinationTableName = 
        "dbo.BulkCopyDemoMatchingColumns";

    try
    {
        // Write from the source to the destination.
        bulkCopy.WriteToServer(ExitingSqlTableName);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
3
jaleel

Je préfère le type de données défini par l'utilisateur: il est super rapide.

Étape 1: créer une table définie par l'utilisateur dans Sql Server DB

CREATE TYPE [dbo].[udtProduct] AS TABLE(
  [ProductID] [int] NULL,
  [ProductName] [varchar](50) NULL,
  [ProductCode] [varchar](10) NULL
)
GO

Étape 2: créer une procédure stockée avec un type défini par l'utilisateur

CREATE PROCEDURE ProductBulkInsertion 
@product udtProduct readonly
AS
BEGIN
    INSERT INTO Product
    (ProductID,ProductName,ProductCode)
    SELECT ProductID,ProductName,ProductCode
    FROM @product
END

Étape 3: exécuter la procédure stockée à partir de c #

SqlCommand sqlcmd = new SqlCommand("ProductBulkInsertion", sqlcon);
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("@product", productTable);
sqlcmd.ExecuteNonQuery();

Problème possible: modifier la table définie par l'utilisateur

En fait, il n'y a pas de commande SQL Server pour modifier le type défini par l'utilisateur. Mais dans Management Studio, vous pouvez y parvenir à partir des étapes suivantes

1. générer un script pour le type (dans une nouvelle fenêtre de requête ou sous forme de fichier) 2. supprimer la table défiée par l'utilisateur. 3. modifiez le script de création, puis exécutez-le.

1
Shamseer K

Si peut s'écarter un peu du chemin droit de DataTable -> table SQL, cela peut aussi se faire via une liste d'objets:

1) DataTable -> Liste générique d'objets

public static DataTable ConvertTo<T>(IList<T> list)
{
    DataTable table = CreateTable<T>();
    Type entityType = typeof(T);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

    foreach (T item in list)
    {
        DataRow row = table.NewRow();

        foreach (PropertyDescriptor prop in properties)
        {
            row[prop.Name] = prop.GetValue(item);
        }

        table.Rows.Add(row);
    }

    return table;
}

La source et plus de détails peuvent être trouvés ici . Les propriétés manquantes conserveront leurs valeurs par défaut (0 pour ints, null pour les types de référence, etc.)

2) Poussez les objets dans la base de données

Une façon consiste à utiliser EntityFramework.BulkInsert extension. Un contexte de données EF est cependant requis.

Il génère la commande BULK INSERT requise pour l'insertion rapide (la solution de type table définie par l'utilisateur est beaucoup plus lente que cela).

Bien que ce ne soit pas la méthode directe, cela aide à construire une base de travail avec une liste d'objets au lieu de DataTables qui semble être beaucoup plus efficace en mémoire .

1
Alexei

Vous pouvez le faire avec des paramètres de valeur de table.

Jetez un œil à l'article suivant:

http://www.codeproject.com/Articles/39161/C-and-Table-Value-Parameters

0
MrEyes