web-dev-qa-db-fra.com

Une façon de SQLBulkCopy "insérer ou mettre à jour s'il existe"?

J'ai besoin de mettre à jour une très grande table périodiquement et SQLBulkCopy est parfait pour cela, seulement que j'ai un index à 2 colonnes qui empêche les doublons. Existe-t-il un moyen d'utiliser SQLBulkCopy comme "insérer ou mettre à jour s'il existe"?

Sinon, quelle est la manière la plus efficace de le faire? Encore une fois, je parle d'une table avec des millions d'enregistrements.

Je vous remercie

22
Sol

Je chargerais les données en bloc dans une table de transfert temporaire, puis ferais une conversion dans la table finale. Voir http://www.databasejournal.com/features/mssql/article.php/3739131/UPSERT-Functionality-in-SQL-Server-2008.htm pour un exemple de réalisation d'un upsert.

10
btilly

J'ai publié un package nuget (SqlBulkTools) pour résoudre ce problème.

Voici un exemple de code qui permettrait d'obtenir un upsert en vrac.

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books")
            .AddAllColumns()
            .BulkInsertOrUpdate()
            .MatchTargetOn(x => x.ISBN)
            .Commit(conn);
    }

    trans.Complete();
}

Pour les très grandes tables, il existe des options pour ajouter des verrous de table et désactiver temporairement les index non clusterisés. Voir Documentation SqlBulkTools pour plus d'exemples.

12
Greg R Taylor

Pas en une seule étape, mais dans SQL Server 2008 , vous pouvez:

  • chargement en vrac dans la table intermédiaire
  • appliquer une instruction MERGE pour mettre à jour/insérer dans votre vraie table

En savoir plus sur instruction MERGE

11
marc_s

Au lieu de créer une nouvelle table temporaire, BTW consomme plus d'espace et de mémoire.

J'ai créé un déclencheur avec INSTEAD OF INSERT et utilisé à l'intérieur de l'instruction MERGE.

Mais n'oubliez pas d'ajouter le paramètre SqlBulkCopyOptions.FireTriggers dans le SqlBulkCopy.

Ceci est mes deux cents.

5
Ivan Paniagua

Une autre alternative serait de ne pas utiliser une table temporaire mais d'utiliser une procédure stockée avec un paramètre de valeur de table. Passez un datatable au sp et faites la fusion là.

1
jb5253

Vous avez un indice de @Ivan. Pour ceux qui pourraient en avoir besoin, voici ce que j'ai fait.

create trigger yourschma.Tr_your_triger_name
    on yourschma.yourtable
    instead of INSERT
    as
    merge into yourschma.yourtable as target
    using inserted as source
    on (target.yourtableID = source.yourtableID)
    when matched then
        update
        set target.ID     = source.ID,
            target.some_column = source.some_column,
            target.Amount                       = source.Amount
    when not matched by target then
        insert (some_column, Amount)
        values (source.some_column, source.Amount);
go
0
Shengfeng Li