Long-running transactions are transactions that create and hold locks for a long time. They include large-scale updates, deletes, and massive inserts. Monthly maintenance programs that move data into an archive directory are a typical example. For instance, Listing 13.7 would move the Orders and Order Details for a month from active to (make-believe) archive tables.
Line 3 declares a table variable. Lines 4 through 8 write a list of OrderIDs to the table variable. We'll use that list of IDs as a reference in the rest of the batch. Notice the use of the readpast hint. If an OrderID is locked, the query will ignore the lock and continue to find orders. You might also want to use the xlock table hint to acquire persistent, exclusive locks.
The INSERT statement in lines 9 through 21 moves the Orders rows to the Orders_ Archive. Lines 22 through 26 moves Order details to the Order Details_Archive. Lines 27 through 30 deletes the rows, first from Order Details, then from Orders (to preserve DRI). The batch is written as a transaction to avoid performing partial work.
Long-running transactions are a significant performance concern for system designers and application programmers. Here's why:
These processes typically modify a large percentage of the data in a table. When that happens, the server often promotes row and page locks to a table lock to minimize lock maintenance overhead. Live, exclusive table locks on active tables force all other processes to wait.
When you update a large amount of data, you also write a significant number of entries to the transaction log. A large active log takes up more space. The log segment will expand until the transaction is complete, or the disk is full.
Note
When you create a database, you decide whether to restrict the growth of the log file(s). If you choose unrestricted file growth, the log file(s) can grow until the logical volume is full. At that point, you get a log-full error message and possibly an out of disk space error as well.
If you are running on a single-CPU system, long-running transactions tend to monopolize the processor.
To avoid long-running transactions, break up work into smaller chunks. For instance, when you perform a monthly archive, write the work one day at a time in a loop. Commit the transactions after each day's work, and consider adding a statement to trun cate the log in the loop. Listing 13.8 contains the skeleton of a revised statement, with the inserts and deletes omitted.
The while loop breaks down each of the large queries into a smaller set of queries. The BACKUP TRANSACTION statement (line 17) truncates the transaction log without performing a backup. This will keep the size of the log under control by freeing up log space between transactions. The BACKUP DATABASE command (line 20) would perform a full backup when the process is complete. Truncating the transaction log disables transactional disaster recovery from that point forward, so you will need to make a new database backup as soon as the process is complete.
Tip
Whenever possible, perform long-running and large-scale work when other users are not working. Your processes will run more smoothly because your table locks won't be blocked by other users. They will be more cheerful because they won't need access to data while you have it locked.