2013-01-25 2 views
2

Je travaille avec 2 serveurs SQL 2008 sur différentes machines. Les noms de serveur sont source.ex.com et destination.ex.com.SqlBulkInsert avec un DataTable à un serveur lié

destination.ex.com est lié à source.ex.com et les autorisations appropriées sont en place pour source.ex.com d'écrire dans une base de données appelée bacon-wrench sur destination.ex.com

Je suis connecté en source.ex.com par SMS et testé cette requête (avec succès):

INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
(PunchID, BaconID) VALUES (4,6); 

Dans un C# .NET 4.0 WebPage je me connecte à source.ex.com et exécuter une requête similaire (avec succès):

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString)) 
{ 
    c.Open(); 
    String sql = @" 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES (34,56);"; 
    using(SqlCommand cmd = new SqlCommand(sql, c)) 
    { 
     cmd.ExecuteNonQuery(); 
    } 
} 

Pour les petits ensembles d'instructions d'insertion (par exemple 20 ou moins) faire quelque chose comme ça joue bien:

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString)) 
{ 
    c.Open(); 
    String sql = @" 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES (34,56); 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES (22,11); 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES (33,55); 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES (1,2);"; 
    using(SqlCommand cmd = new SqlCommand(sql, c)) 
    { 
     cmd.ExecuteNonQuery(); 
    } 
} 

Je suis en train de faire quelque chose comme ça avec environ 20 000 dossiers. La méthode ci-dessus prend 11 minutes à compléter - ce que je suppose est le serveur qui me sreaming pour en faire une sorte d'opération en vrac. A partir d'autres threads StackOverflow, la classe SqlBulkCopy a été recommandée et prend comme paramètre DataTable, parfait!

Alors je construis un DataTable et d'essayer d'écrire sur le serveur (échec):

DataTable dt = new DataTable(); 
dt.Columns.Add("PunchID", typeof(int)); 
dt.Columns.Add("BaconID", typeof(int)); 
for(int i = 0; i < 20000; i++) 
{ 
    //I realize this would make 20000 duplicate 
    //rows but its not important 
    dt.Rows.Add(new object[] { 
     11, 33 
    }); 
} 

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString)) 
{ 
    c.Open(); 
    using(SqlBulkCopy bulk = new SqlBulkCopy(c)) 
    { 
     bulk.DestinationTableName = "[destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch]"; 
     bulk.ColumnMappings.Add("PunchID", "PunchID"); 
     bulk.ColumnMappings.Add("BaconID", "BaconID"); 
     bulk.WriteToServer(dt); 
    } 
} 

EDIT2: Le message ci-dessous est ce que je tente de corriger:

La plante page web à bulk.WriteToServer(dt); avec un message d'erreur Database bacon-wrench does not exist please ensure it is typed correctly. Qu'est-ce que je fais mal? Comment puis-je changer cela pour le faire fonctionner?

EDIT1:

j'ai pu accélérer la requête de manière significative en utilisant la syntaxe ci-dessous. Mais c'est encore très lent pour un si petit ensemble d'enregistrements.

using(SqlConnection c = new SqlConnection(ConfigurationManager.ConnectionStrings["SOURCE"].ConnectionString)) 
{ 
    c.Open(); 
    String sql = @" 
     INSERT INTO [destination.ex.com].[bacon-wrench].[dbo].[tblFruitPunch] 
     (PunchID, BaconID) VALUES 
     (34,56), 
     (22,11), 
     (33,55), 
     (1,2);"; 
    using(SqlCommand cmd = new SqlCommand(sql, c)) 
    { 
     cmd.ExecuteNonQuery(); 
    } 
} 

Répondre

0

Après avoir essayé un certain nombre de choses, y compris les paramètres du serveur liés, classements, synonymes, etc., j'ai finalement arrivé à ce message d'erreur:

Inserting into remote tables or views is not allowed by using the BCP utility or by using BULK INSERT.

Peut-être que vous pouvez insérer en vrac à une mise en scène table sur votre serveur local (votre code fonctionne bien pour cela), puis insérez à partir de cette table de transfert vers votre serveur lié à partir de là, suivi d'une suppression locale de la table de transfert. Vous devrez tester la performance.

+0

Oh Noes! Je vais essayer comme vous l'avez suggéré. –

0

Si vous utilisez SQL Server 2008+, vous pouvez introduire un type de données utilisateur Table. Préparez le type, la table de réception et la procédure stockée quelque chose comme ci-dessous. Le type de données et la procédure stockée sont sur le système local. J'ai généralement une instruction if dans le code qui détecte si la table est distante ou locale, à distance je le fais, local j'utilise SqlBulkCopy.

if(TYPE_ID(N'[Owner].[TempTableType]') is null) 
begin 
CREATE TYPE [Owner].[TempTableType] AS TABLE ([PendingID] uniqueidentifier, [Reject] bit) 
end 
IF NOT EXISTS (SELECT * FROM [LinkedServer].[DatabaseOnLS].sys.tables where name = 'TableToReceive') 
EXEC(' 
CREATE TABLE [DatabaseOnLS].[Owner].[TableToReceive] ([PendingID] uniqueidentifier, [Reject] bit) 
') AT [LinkedServer] 
else 
EXEC(' 
TRUNCATE TABLE [DatabaseOnLS].[Owner].[TableToReceive] 
') AT [LinkedServer] 

CREATE PROCEDURE [Owner].[TempInsertTable] 
@newTableType TempTableType readonly 
AS 
BEGIN 
insert into [LinkedServer].[DatabaseOnLS].[Owner].[TableToReceive] select * from @newTableType 
END 

Dans le code C#, vous pouvez faire quelque chose comme cela pour insérer le DataTable dans la table sur le serveur lié (j'utilise une UnitOfWork existante, qui ont déjà une connexion et transaction):

using (var command = new SqlCommand("TempInsertTable", 
        oUoW.Database.Connection as SqlConnection) { CommandType = CommandType.StoredProcedure } 
       ) 
{ 
    command.Transaction = oUoW.Database.CurrentTransaction as SqlTransaction; 
    command.Parameters.Add(new SqlParameter("@newTableType", oTempTable)); 
    drResults = command.ExecuteReader(); 
    drResults.Close(); 
} 
Questions connexes