Table of Contents
Previous Section Next Section

Transactions at Work

Here, we'll provide a closer look at how transactions are supported by the database products you've been investigating so far.

SQL Server

SQL Server works by default in autocommit mode, with each SQL command treated as a separate transaction, unless you use BEGIN TRANSACTION to start a multistatement transaction.

Tip 

You can set SQL Server to work with implicit transactions (by turning off autocommit mode) by using SET IMPLICIT_TRANSACTIONS ON. When working in implicit transactions mode, SQL Server assumes the BEGIN TRANSACTION command before the first SQL query is issued. In the following examples, we assume you'll work with the default transactions mode of SQL Server, which requires you to manually start multistatement transactions with BEGIN TRANSACTION.

The statements that deal with transactions in SQL Server are as follows:

BEGIN TRANSACTION [<transaction name>]
COMMIT TRANSACTION [<transaction name>]
ROLLBACK TRANSACTION [<transaction name>|<savepoint name>]
SAVE TRANSACTION <savepoint name>

The TRANSACTION keyword can be optionally replaced by its shorter form, TRAN.

BEGIN, COMMIT, and ROLLBACK receive an optional transaction name. Using names for transactions doesn't bring any new functionality, but it can help for readability if you use suggestive names, especially in stored procedures or batches, that have more transactions.

If ROLLBACK doesn't have a parameter or if the parameter specifies a transaction name, the whole transaction is rolled back. If a savepoint name is received instead, the transaction is rolled back to the specified savepoint.

Note that after rolling back to a savepoint, the transaction is still active. It still needs a final ROLLBACK or COMMIT, with either no parameter or with the transaction's name as a parameter to finalize the transaction.

SQL SERVER TRANSACTIONS AND SAVEPOINTS
Start example

In this example, you'll see a simple SQL Server transaction. In this transaction, you'll add two new students to the Student table from the InstantUniversity database. However, the second INSERT operation is rolled back using a savepoint. Finally, the transaction is committed:

BEGIN TRANSACTION MyTransaction;
INSERT INTO Student (StudentID, Name) VALUES (98, 'Good Student');
SAVE TRANSACTION BeforeAddingBadStudent;
INSERT INTO Student (StudentID, Name) VALUES (99, 'Bad Student');
ROLLBACK TRANSACTION BeforeAddingBadStudent;
COMMIT TRANSACTION MyTransaction;
SELECT * FROM Student;

The example starts by declaring the new transaction with this well-known command:

BEGIN TRANSACTION MyTransaction

Then you add Good Student to the Student table:

INSERT INTO Student (StudentID, Name) VALUES (98, 'Good Student')

Then you add yet another student to the table. However, the operation is rolled back using the BeforeAddingBadStudent savepoint:

SAVE TRANSACTION BeforeAddingBadStudent
INSERT INTO Student (StudentID, Name) VALUES (99, 'Bad Student')
ROLLBACK TRANSACTION BeforeAddingBadStudent

Practically, this piece of code actually does nothing because the changes are annulled. Also, after rolling back to a savepoint, the transaction is still active, so you still have the power to commit it or roll it back completely. In the end, you commit the transaction and display the contents of the Employee table:

COMMIT TRANSACTION MyTransaction
SELECT * FROM Student

If you had rolled back the transaction instead of committing it, the changes done by both INSERT statements would have been void, and the Student table would have ended up being just as it was before starting the transaction.

End example

SQL Server Transactions and Error Handling

The first example was interesting enough, but it didn't show how to roll back the whole transaction in case something bad happens in any of the queries (that was the whole point after all, right?).

Let's see how to do that in another example.

SQL SERVER TRANSACTIONS WITH ERROR HANDLING
Start example

In this example, you'll insert a number of rows into the Student table as part of a transaction, and after each insert, you'll check whether an error occurred. If it did, you'll roll back the transaction. If all the inserts succeed, you commit the transaction. In both cases, you display a message saying whether the transaction was committed or rolled back:

BEGIN TRANSACTION MyTransaction

INSERT INTO Student (StudentID, Name) VALUES (101, 'Dave')
IF @@ERROR != 0
BEGIN
   ROLLBACK TRANSACTION MyTransaction
   PRINT 'Cannot insert Dave! Transaction rolled back.'
   RETURN
END
INSERT INTO Student (StudentID, Name) VALUES (102, 'Claire')
IF @@ERROR != 0
BEGIN
   ROLLBACK TRANSACTION MyTransaction
   PRINT 'Cannot insert Claire! Transaction rolled back.'
   RETURN
END

INSERT INTO Student (StudentID, Name) VALUES (103, 'Anne')
IF @@ERROR != 0
BEGIN
   ROLLBACK TRANSACTION MyTransaction
   PRINT 'Cannot insert Anne! Transaction rolled back.'
   RETURN
END

COMMIT TRANSACTION MyTransaction
IF @@ERROR != 0
   PRINT 'Could not COMMIT transaction'
ELSE
   PRINT 'Transaction committed.'

This example looks a bit different from the previous one. The batch of statements tries to insert three records to the Student table. If any of the INSERT statements fail, you roll back the transaction, display a message on the screen, and stop the execution.

You achieve this by verifying the @@ERROR system variable after each statement that could generate an error:

INSERT INTO Student (StudentID, Name) VALUES (101, 'Dave')
IF @@ERROR != 0
BEGIN
   ROLLBACK TRANSACTION MyTransaction
   PRINT 'Cannot insert Dave! Transaction rolled back.'
   RETURN
END

The @@ERROR system variable is automatically updated after each SQL query, so it is necessary to test it each time you do something to the database. Another important thing to note is that you call RETURN after rolling back the transaction. This wouldn't be necessary if you had rolled back to a savepoint, but if you roll back the entire transaction, the execution should stop; otherwise, the following SQL statements would mistakenly assume that they're part of a transaction and call ROLLBACK or COMMIT at certain points.

You can also test the outcome of the COMMIT command. This way you deal with the case where there's a problem and the transaction can't be committed. The most likely reason for this is if you already finalized the transaction (say, rolled back the transaction when an INSERT failed without calling RETURN after ROLLBACK). This is a great debugging technique because it provides feedback about the transaction's output, but once you're confident that the SQL script is well constructed, you can omit this:

COMMIT TRANSACTION MyTransaction
IF @@ERROR != 0
   PRINT 'Could not COMMIT transaction'
ELSE
   PRINT 'Transaction committed.'

The first time the batch is executed, the output should read as follows:

   Transaction committed.

However, if you now execute the batch again, the transaction will be rolled back because you can't insert two rows with the same primary key value into a table:

   Violation of PRIMARY KEY constraint PK__Student__59063A47'.

   Cannot insert duplicate key in object 'Student'.
   The statement has been terminated.
   Cannot insert Dave! Transaction rolled back.
End example

Oracle

Oracle transactions work by default in automatic-transactions mode. In other words, it starts a new multistatement transaction with the first SQL query you type. This mode is used by default in DB2 as well, and it's the mode used in these examples.

COMMIT and ROLLBACK are still your best friends when working in automatic-transactions mode. You create savepoints using the SAVEPOINT command, and if you want to roll back to a savepoint, you must use ROLLBACK TO <savepoint name>.

ORACLE TRANSACTIONS AND SAVEPOINTS
Start example

To start a new transaction, you just need to start executing SQL queries. In this example, you'll add two new students to the Student table. However, the second INSERT operation is rolled back using a savepoint. Finally, you commit the transaction:

INSERT INTO Student (StudentID, Name) VALUES (98, 'Good Student');
SAVEPOINT BeforeAddingBadStudent;
INSERT INTO Student (StudentID, Name) VALUES (99, 'Bad Student');
ROLLBACK TO BeforeAddingBadStudent;
COMMIT;

You added two new students to the Student table. However, before adding the second one, you created a savepoint—which was then used as a point to which you rolled back the transaction. Right now, if you type SELECT * FROM Student, in addition to the original rows, you'll see a single new row, containing Good Student.

End example

Oracle Transactions and Exception Handling

Oracle has a robust and powerful exception handling system, which proves to be helpful when dealing with real-world transactions. When an error occurs in your code, the transaction needs to be rolled back, and the other SQL statements shouldn't execute anymore.

You saw in the SQL Server example that one way to deal with this is to check the outcome of each SQL statement and deal with them separately. Oracle has another way of dealing with this.

ORACLE TRANSACTIONS AND EXCEPTION HANDLING
Start example

In this example, you'll try to insert three rows into the Student table. One of the inserts will be rolled back using a savepoint. If any exceptions are raised in the process, you roll back the whole transaction and bring the Student table back to its original state.

Here's the piece of code that does this:

BEGIN

   INSERT INTO Student (StudentID, Name) VALUES (101, 'Dave');
   INSERT INTO Student (StudentID, Name) VALUES (102, 'Claire');

   SAVEPOINT BeforeAddingAnne;
   INSERT INTO Student (StudentID, Name) VALUES (103, 'Anne');
   ROLLBACK TO BeforeAddingAnne;

   COMMIT;

EXCEPTION
   WHEN OTHERS
      THEN ROLLBACK;
END;
/

After typing the batch, you can save it using SAVE:

SAVE exception

Once the procedure is saved to a file, you can call it like this:

@exception

If any of the SQL queries in the BEGIN block generates an error, the execution is passed to the EXCEPTION block:

EXCEPTION
   WHEN OTHERS
      THEN ROLLBACK;

You can handle separately many different kinds of predefined exceptions, and you can also define and raise your own exceptions. Also, if you have nested BEGIN blocks or stored procedures, the unhandled exceptions propagate vertically. In this case, you have used the WHEN OTHERS option, which handles all exceptions. There you use ROLLBACK to roll back the transaction. You can learn more about Oracle exception handling in Chapter 9.

The procedure you wrote would end up adding two rows into the Student table, as long as no errors are generated inside the script. If an error does occur, everything is brought back to the original state. If, for example, you already have a student with a StudentID of 102 before executing the script, an error will be generated when trying to add Claire, and the transaction is rolled back (so not even Dave will end up being in the Student table).

After you've run this code once, each subsequent time you execute @exception, the transaction will be rolled back.

End example

DB2

DB2 supports the COMMIT, ROLLBACK, and SAVEPOINT commands. Like Oracle, DB2 works in automatic-transactions mode by default.

To switch between automatic-transactions mode and autocommit mode, you need to alter the Command Center options, as shown earlier in this chapter. For the purpose of this example, clear the Automatically Commit SQL Statements checkbox. So, let's look at an example.

DB2 TRANSACTIONS
Start example

In this example, you'll add two new students to the Student table—a good student and a bad student. You reverse the addition of the bad student by using a savepoint:

INSERT INTO Student (StudentID, Name) VALUES (98, 'Good Student');
SAVEPOINT BeforeAddingBadStudent ON ROLLBACK RETAIN CURSORS;
INSERT INTO Student (StudentID, Name) VALUES (99, 'Bad Student');
ROLLBACK TO SAVEPOINT BeforeAddingBadStudent;
COMMIT;

Notice the way you create the savepoint:

SAVEPOINT BeforeAddingBadStudent ON ROLLBACK RETAIN CURSORS;

The ON ROLLBACK RETAIN CURSORS statement ensures that your code will continue to execute from the point where the rollback was called after the rollback has been performed.

End example

MySQL

MySQL does have support for transactions, but only if you know how to create your data tables. Yes, this sounds a bit weird, but it's true.

The MySQL engine supports more internal data storage formats for its data tables. When creating a new data table with CREATE TABLE, the default table type is used, which is MyISAM. This table type is pretty basic and doesn't support features such as transactions or the capability to enforce referential integrity through foreign keys, but it's the fastest one available for MySQL.

In total, MySQL supports at least five table types: MyISAM, HEAP, ISAM, BDB, and InnoDB. For detailed information about each table type supported by MySQL, please visit http://www.mysql.com/doc/en/Table_types.html.

Tip 

MySQL documentation recommends using the default table type, MyISAM, if transactions aren't required because it works much faster without the overhead of keeping a transaction log.

The important fact to understand, for the purposes of this chapter, is that BDB and InnoDB are the only types that support transactions. InnoDB fully supports the ACID properties, and as such, we'll be using this table type here. You can learn more about this table type at http://www.mysql.com/doc/en/InnoDB.html.

By default, MySQL works in autocommit mode (like SQL Server does). Each update on the database is committed immediately, without the need to call COMMIT. To explicitly start a multistatement transaction, you use BEGIN, and you finish the transaction with either COMMIT or ROLLBACK. MySQL doesn't support savepoints.

For transaction-safe tables (BDB and InnoDB), you can also instruct MySQL to work in non-autocommit mode (just like Oracle works by default) using SET AUTOCOMMIT=0.

MYSQL TRANSACTIONS
Start example

Let's look at a simple example with MySQL transactions. Start a new transaction with BEGIN, add two new students to the Student table, and then roll back the transaction. Open a new MySQL console, open the InstantUniversity database, and type the following:

BEGIN;

INSERT INTO Student (StudentID, Name) VALUES (98, 'Anne');
INSERT INTO Student (StudentID, Name) VALUES (99, 'Julian');

ROLLBACK;

Now, read the Student table, and you'll see that it has no new rows—the transaction was successfully rolled back.

End example

Access

This is really simple: Microsoft Access doesn't support transactions, so there's not a lot to discuss here!


Table of Contents
Previous Section Next Section