Udostępnij przez


Operacje transakcji i kopiowania zbiorczego

Pobieranie ADO.NET

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;";
    }
}

Dalsze kroki