Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Operacje kopiowania zbiorczego można wykonywać jako operacje izolowane lub w ramach transakcji wielokrotnego kroku. Ta ostatnia opcja umożliwia wykonywanie więcej niż jednej operacji kopiowania zbiorczego w ramach tej samej transakcji, a także wykonywanie innych operacji bazy danych, takich jak operacje wstawiania, aktualizacji i usuwania, przy jednoczesnym zachowaniu możliwości zatwierdzenia lub wycofania całej transakcji.
Domyślnie operacja kopiowania zbiorczego jest wykonywana jako izolowana operacja. Operacja kopiowania zbiorczego odbywa się w sposób nietransakcyjny, bez możliwości wycofania zmian. Jeśli musisz wycofać całą lub część kopii zbiorczej po wystąpieniu błędu, możesz:
Użyj transakcji zarządzanej SqlBulkCopy
Wykonywanie operacji kopiowania zbiorczego w ramach istniejącej transakcji
Dołączyć do System.TransactionsTransaction.
Wykonywanie operacji kopiowania zbiorczego bez transakcji
W poniższej aplikacji konsolowej pokazano, co się stanie, gdy operacja kopiowania zbiorczego bez transakcji napotka błąd podczas operacji.
W tym przykładzie tabela źródłowa i tabela docelowa zawierają kolumnę Identity o nazwie ProductID. Kod najpierw przygotowuje tabelę docelową, usuwając wszystkie wiersze, a następnie wstawiając pojedynczy wiersz, którego ProductID jest potwierdzony jako istniejący w tabeli źródłowej. Domyślnie nowa wartość kolumny Identity jest generowana w tabeli docelowej dla każdego dodanego wiersza. W tym przykładzie opcja jest ustawiana po otwarciu połączenia, które wymusza proces ładowania zbiorczego na użycie wartości Identity z tabeli źródłowej.
Operacja kopiowania zbiorczego jest wykonywana z właściwością BatchSize ustawioną na 10. Gdy operacja napotka nieprawidłowy wiersz, zostanie zgłoszony wyjątek. W tym pierwszym przykładzie operacja kopiowania zbiorczego nie jest transaktoryzowana. Wszystkie pakiety, które zostały skopiowane do momentu wystąpienia błędu, są zatwierdzane. Partia zawierająca zduplikowany klucz jest cofana, a operacja kopiowania zbiorczego jest zatrzymywana przed przetwarzaniem pozostałych partii.
Uwaga / Notatka
Ten przykład kodu nie zostanie uruchomiony, chyba że utworzono tabele robocze zgodnie z opisem w Konfiguracja przykładu kopiowania zbiorczego. Ten kod jest dostarczany w celu zademonstrowania składni tylko przy użyciu narzędzia SqlBulkCopy . Jeśli tabele źródłowe i docelowe znajdują się w tym samym wystąpieniu programu SQL Server, łatwiej i szybciej używać instrukcji języka Transact-SQL INSERT ... SELECT do kopiowania danych.
using Microsoft.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = GetConnectionString();
// Open a sourceConnection to the AdventureWorks database.
using (SqlConnection sourceConnection =
new SqlConnection(connectionString))
{
sourceConnection.Open();
// Delete all from the destination table.
SqlCommand commandDelete = new SqlCommand();
commandDelete.Connection = sourceConnection;
commandDelete.CommandText =
"DELETE FROM dbo.BulkCopyDemoMatchingColumns";
commandDelete.ExecuteNonQuery();
// Add a single row that will result in duplicate key
// when all rows from source are bulk copied.
// Note that this technique will only be successful in
// illustrating the point if a row with ProductID = 446
// exists in the AdventureWorks Production.Products table.
// If you have made changes to the data in this table, change
// the SQL statement in the code to add a ProductID that
// does exist in your version of the Production.Products
// table. Choose any ProductID in the middle of the table
// (not first or last row) to best illustrate the result.
SqlCommand commandInsert = new SqlCommand();
commandInsert.Connection = sourceConnection;
commandInsert.CommandText =
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns ON;" +
"INSERT INTO " + "dbo.BulkCopyDemoMatchingColumns " +
"([ProductID], [Name] ,[ProductNumber]) " +
"VALUES(446, 'Lock Nut 23','LN-3416');" +
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns OFF";
commandInsert.ExecuteNonQuery();
// Perform an initial count on the destination table.
SqlCommand commandRowCount = new SqlCommand(
"SELECT COUNT(*) FROM dbo.BulkCopyDemoMatchingColumns;",
sourceConnection);
long countStart = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Starting row count = {0}", countStart);
// Get data from the source table as a SqlDataReader.
SqlCommand commandSourceData = new SqlCommand(
"SELECT ProductID, Name, ProductNumber " +
"FROM Production.Product;", sourceConnection);
SqlDataReader reader = commandSourceData.ExecuteReader();
// Set up the bulk copy object using the KeepIdentity option.
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
connectionString, SqlBulkCopyOptions.KeepIdentity))
{
bulkCopy.BatchSize = 10;
bulkCopy.DestinationTableName =
"dbo.BulkCopyDemoMatchingColumns";
// Write from the source to the destination.
// This should fail with a duplicate key error
// after some of the batches have been copied.
try
{
bulkCopy.WriteToServer(reader);
}
catch (Exception ex)
{
// Print the number of rows processed using the
// RowsCopied property.
Console.WriteLine("{0} rows were processed.",
bulkCopy.RowsCopied);
Console.WriteLine(ex.Message);
}
finally
{
reader.Close();
}
}
// Perform a final count on the destination
// table to see how many rows were added.
// Note that for this scenario, the value will
// not be equal to the RowsCopied property.
long countEnd = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Ending row count = {0}", countEnd);
Console.WriteLine("{0} rows were added.", countEnd - countStart);
Console.WriteLine("Press Enter to finish.");
Console.ReadLine();
}
}
private static string GetConnectionString()
// To avoid storing the sourceConnection string in your code,
// you can retrieve it from a configuration file.
{
return "Data Source=(local); " +
" Integrated Security=true;" +
"Initial Catalog=AdventureWorks;";
}
}
Wykonywanie dedykowanej operacji kopiowania zbiorczego w transakcji
Domyślnie operacja kopiowania zbiorczego jest własną transakcją. Jeśli chcesz wykonać dedykowaną operację kopiowania zbiorczego, utwórz instancję SqlBulkCopy z użyciem ciągu połączenia lub użyj istniejącego obiektu SqlConnection bez aktywnej transakcji. Operacja kopiowania zbiorczego tworzy transakcję w każdym scenariuszu, a następnie zatwierdza lub cofa transakcję.
Możesz jawnie określić opcję UseInternalTransaction w konstruktorze klasy SqlBulkCopy, aby wykonać operację kopiowania zbiorczego we własnej transakcji. Każda partia operacji zostanie wykonana w ramach oddzielnej transakcji.
Uwaga / Notatka
Ponieważ różne serie są wykonywane w różnych transakcjach, jeśli wystąpi błąd podczas zbiorczego kopiowania danych, wszystkie wiersze w bieżącej serii zostaną wycofane, ale wiersze z poprzednich serii pozostaną w bazie danych.
Następująca aplikacja konsolowa jest podobna do poprzedniego przykładu z jednym wyjątkiem: W tym przykładzie operacja kopiowania zbiorczego zarządza własnymi transakcjami. Wszystkie pakiety, które zostały skopiowane do momentu wystąpienia błędu, są zatwierdzane. Partia zawierająca zduplikowany klucz jest cofana, a operacja kopiowania zbiorczego jest zatrzymywana przed przetwarzaniem pozostałych partii.
Ważne
Ten przykład kodu nie zostanie uruchomiony, chyba że utworzono tabele robocze zgodnie z opisem w Konfiguracja przykładu kopiowania zbiorczego. Ten kod jest dostarczany w celu zademonstrowania składni tylko przy użyciu narzędzia SqlBulkCopy . Jeśli tabele źródłowe i docelowe znajdują się w tym samym wystąpieniu programu SQL Server, łatwiej i szybciej jest użyć instrukcji Transact-SQL INSERT ... SELECT do skopiowania danych.
using Microsoft.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = GetConnectionString();
// Open a sourceConnection to the AdventureWorks database.
using (SqlConnection sourceConnection =
new SqlConnection(connectionString))
{
sourceConnection.Open();
// Delete all from the destination table.
SqlCommand commandDelete = new SqlCommand();
commandDelete.Connection = sourceConnection;
commandDelete.CommandText =
"DELETE FROM dbo.BulkCopyDemoMatchingColumns";
commandDelete.ExecuteNonQuery();
// Add a single row that will result in duplicate key
// when all rows from source are bulk copied.
// Note that this technique will only be successful in
// illustrating the point if a row with ProductID = 446
// exists in the AdventureWorks Production.Products table.
// If you have made changes to the data in this table, change
// the SQL statement in the code to add a ProductID that
// does exist in your version of the Production.Products
// table. Choose any ProductID in the middle of the table
// (not first or last row) to best illustrate the result.
SqlCommand commandInsert = new SqlCommand();
commandInsert.Connection = sourceConnection;
commandInsert.CommandText =
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns ON;" +
"INSERT INTO " + "dbo.BulkCopyDemoMatchingColumns " +
"([ProductID], [Name] ,[ProductNumber]) " +
"VALUES(446, 'Lock Nut 23','LN-3416');" +
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns OFF";
commandInsert.ExecuteNonQuery();
// Perform an initial count on the destination table.
SqlCommand commandRowCount = new SqlCommand(
"SELECT COUNT(*) FROM dbo.BulkCopyDemoMatchingColumns;",
sourceConnection);
long countStart = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Starting row count = {0}", countStart);
// Get data from the source table as a SqlDataReader.
SqlCommand commandSourceData = new SqlCommand(
"SELECT ProductID, Name, ProductNumber " +
"FROM Production.Product;", sourceConnection);
SqlDataReader reader = commandSourceData.ExecuteReader();
// Set up the bulk copy object.
// Note that when specifying the UseInternalTransaction
// option, you cannot also specify an external transaction.
// Therefore, you must use the SqlBulkCopy construct that
// requires a string for the connection, rather than an
// existing SqlConnection object.
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
connectionString, SqlBulkCopyOptions.KeepIdentity |
SqlBulkCopyOptions.UseInternalTransaction))
{
bulkCopy.BatchSize = 10;
bulkCopy.DestinationTableName =
"dbo.BulkCopyDemoMatchingColumns";
// Write from the source to the destination.
// This should fail with a duplicate key error
// after some of the batches have been copied.
try
{
bulkCopy.WriteToServer(reader);
}
catch (Exception ex)
{
// Print the number of rows processed using the
// RowsCopied property.
Console.WriteLine("{0} rows were processed.",
bulkCopy.RowsCopied);
Console.WriteLine(ex.Message);
}
finally
{
reader.Close();
}
}
// Perform a final count on the destination
// table to see how many rows were added.
// Note that for this scenario, the value will
// not be equal to the RowsCopied property.
long countEnd = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Ending row count = {0}", countEnd);
Console.WriteLine("{0} rows were added.", countEnd - countStart);
Console.WriteLine("Press Enter to finish.");
Console.ReadLine();
}
}
private static string GetConnectionString()
// To avoid storing the sourceConnection string in your code,
// you can retrieve it from a configuration file.
{
return "Data Source=(local); " +
" Integrated Security=true;" +
"Initial Catalog=AdventureWorks;";
}
}
Korzystanie z istniejących transakcji
Istniejący obiekt SqlTransaction można określić jako parametr w konstruktorze SqlBulkCopy. W takiej sytuacji operacja kopiowania zbiorczego jest wykonywana w istniejącej transakcji i nie jest wprowadzana żadna zmiana stanu transakcji — nie jest zatwierdzana ani przerywana. Ta metoda umożliwia aplikacji uwzględnienie operacji kopiowania zbiorczego w transakcji z innymi operacjami bazy danych. Jeśli jednak nie określisz obiektu SqlTransaction i przekażesz odwołanie typu null, a połączenie ma aktywną transakcję, zostanie zgłoszony wyjątek.
Jeśli musisz wycofać całą operację kopiowania zbiorczego, ponieważ wystąpi błąd lub jeśli kopia zbiorcza powinna zostać wykonana w ramach większego procesu, który można wycofać, możesz podać obiekt SqlTransaction konstruktorowi SqlBulkCopy.
Następująca aplikacja konsolowa jest podobna do pierwszej (niezwiązanej z transakcjami), z jednym wyjątkiem: w tym przykładzie operacja kopiowania zbiorczego jest uwzględniana w większej transakcji zewnętrznej. Gdy wystąpi błąd naruszenia klucza podstawowego, cała transakcja zostanie wycofana i do tabeli docelowej nie zostaną dodane żadne wiersze.
Ważne
Ten przykład kodu nie zostanie uruchomiony, chyba że utworzono tabele robocze zgodnie z opisem w Konfiguracja przykładu kopiowania zbiorczego. Ten kod jest dostarczany w celu zademonstrowania składni tylko przy użyciu narzędzia SqlBulkCopy . Jeśli tabele źródłowe i docelowe znajdują się w tym samym wystąpieniu programu SQL Server, łatwiej i szybciej używać instrukcji języka Transact-SQL INSERT ... SELECT do kopiowania danych.
using Microsoft.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = GetConnectionString();
// Open a sourceConnection to the AdventureWorks database.
using (SqlConnection sourceConnection =
new SqlConnection(connectionString))
{
sourceConnection.Open();
// Delete all from the destination table.
SqlCommand commandDelete = new SqlCommand();
commandDelete.Connection = sourceConnection;
commandDelete.CommandText =
"DELETE FROM dbo.BulkCopyDemoMatchingColumns";
commandDelete.ExecuteNonQuery();
// Add a single row that will result in duplicate key
// when all rows from source are bulk copied.
// Note that this technique will only be successful in
// illustrating the point if a row with ProductID = 446
// exists in the AdventureWorks Production.Products table.
// If you have made changes to the data in this table, change
// the SQL statement in the code to add a ProductID that
// does exist in your version of the Production.Products
// table. Choose any ProductID in the middle of the table
// (not first or last row) to best illustrate the result.
SqlCommand commandInsert = new SqlCommand();
commandInsert.Connection = sourceConnection;
commandInsert.CommandText =
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns ON;" +
"INSERT INTO " + "dbo.BulkCopyDemoMatchingColumns " +
"([ProductID], [Name] ,[ProductNumber]) " +
"VALUES(446, 'Lock Nut 23','LN-3416');" +
"SET IDENTITY_INSERT dbo.BulkCopyDemoMatchingColumns OFF";
commandInsert.ExecuteNonQuery();
// Perform an initial count on the destination table.
SqlCommand commandRowCount = new SqlCommand(
"SELECT COUNT(*) FROM dbo.BulkCopyDemoMatchingColumns;",
sourceConnection);
long countStart = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Starting row count = {0}", countStart);
// Get data from the source table as a SqlDataReader.
SqlCommand commandSourceData = new SqlCommand(
"SELECT ProductID, Name, ProductNumber " +
"FROM Production.Product;", sourceConnection);
SqlDataReader reader = commandSourceData.ExecuteReader();
//Set up the bulk copy object inside the transaction.
using (SqlConnection destinationConnection =
new SqlConnection(connectionString))
{
destinationConnection.Open();
using (SqlTransaction transaction =
destinationConnection.BeginTransaction())
{
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
destinationConnection, SqlBulkCopyOptions.KeepIdentity,
transaction))
{
bulkCopy.BatchSize = 10;
bulkCopy.DestinationTableName =
"dbo.BulkCopyDemoMatchingColumns";
// Write from the source to the destination.
// This should fail with a duplicate key error.
try
{
bulkCopy.WriteToServer(reader);
transaction.Commit();
}
catch (Exception ex)
{
// Print the number of rows processed using the
// RowsCopied property.
Console.WriteLine("{0} rows were processed.",
bulkCopy.RowsCopied);
Console.WriteLine(ex.Message);
transaction.Rollback();
}
finally
{
reader.Close();
}
}
}
}
// Perform a final count on the destination
// table to see how many rows were added.
long countEnd = System.Convert.ToInt32(
commandRowCount.ExecuteScalar());
Console.WriteLine("Ending row count = {0}", countEnd);
Console.WriteLine("{0} rows were added.", countEnd - countStart);
Console.WriteLine("Press Enter to finish.");
Console.ReadLine();
}
}
private static string GetConnectionString()
// To avoid storing the sourceConnection string in your code,
// you can retrieve it from a configuration file.
{
return "Data Source=(local); " +
" Integrated Security=true;" +
"Initial Catalog=AdventureWorks;";
}
}