© 2003 International Business Machines Corporation. All rights reserved.
When certain events take place in an IBM® DB2® Universal DatabaseTM database, you can fire triggers that can perform other actions. In this article, you'll explore the world of triggers and see how they can be used to enforce business rules in your database. You'll also learn how the DB2 UDB Version 8.1 Control Center can help you build a simple trigger that can be applied to a simple business scenario.
When a specified SQL operation -- a DELETE, INSERT, or UPDATE -- occurs on a table, a trigger can be activated that defines a set of actions. Triggers, unlike referential and check constraints, can even be used to update other tables.
It always helps to apply a technology to a real-world scenario. For educational purposes, let's study triggers in the context of a bank that has a simple setup of just one table. Again, I did say this was simplified! What we will be doing with triggers is facilitating the overdraft protection that some banks offer. For example, a bank customer has a checking account and a savings account. When an amount is withdrawn from their checking account that exceeds the balance of the checking account, an automated transfer of funds (coined overdraft protection) can take place that transfers money from the customer's savings account. Of course, conditions must be checked to ensure that the savings account in question has enough money in it to make up for the overage.
As mentioned above, our bank will consist of only one table. In it, we'll house information about a customer's checking account and savings account balances. Each customer is identified by their social security number. Here is a description of the table:
Table 1. Description of ACCTTABLE
|Column Name||Column Type||Nullable?|
|SavingBalance||Decimal (Precision: 7, Scale: 2)||NO|
|CheckingBalance||Decimal (Precision: 7, Scale: 2)||NO|
* denotes primary key
Go ahead and use the DB2 Command Line Processor to create a database for the table above. Call the database
db2 => create database bnkdb
Next, connect to the database. I am going to assume you have a user on your machine of the name
db2admin with a password of
db2 => connect to bnkdb user db2admin using db2admin
Now, create the
db2 => create table accttable(ssn varchar(30) not null primary key, lastname varchar(30) not null, firstname varchar(30) not null, savingbalance decimal(7,2) not null, checkingbalance decimal(7,2) not null)
Now go ahead and add a couple of entries to the table:
db2 => insert into accttable values ('111-11-1111','Bhogal','Kulvir',1500.00,1000)
db2 => insert into accttable values ('222-22-2222','Guy','Someother',2000.00,4000)
Triggers can be fired before or after an INSERT, DELETE, or UPDATE of a table. In our example, you'll be creating a trigger that fires before the UPDATE of the ACCTTABLE. In trigger terminology, the INSERT, DELETE, or UPDATE that causes a trigger to be fired is known as the triggering event. Whether a trigger fires before or after the triggering event is known as the activation time of the trigger.
Start creating the trigger by opening up the DB2 Control Center, expanding the database you created (that is, bnkdb) and right clicking on the Triggers option and choosing Create.....
Figure 1. Create trigger
On the Create Trigger screen, you'll be able to specify the schema your trigger resides in. Go ahead and choose the DB2ADMIN schema. Remember, triggers are associated with tables, so we need to choose the schema of the associated table. Again go ahead and choose the DB2ADMIN schema:
Figure 2. Choose a schema
In the same screen, you will need to specify a trigger name. Specify a trigger name of OVERDRAFT. Also, specify the table name that is associated with the trigger. Pick the table you created called ACCTTABLE.
Figure 3. Pick the table you created
In the Time to trigger action area, choose Before.
Figure 4. Choose Before
In the section called Operation that causes the trigger to be executed choose the Update of columns function and specify the column of CHECKINGBALANCE:
Click the Triggered action tab to further build the trigger:
Figure 6. Build the trigger
DB2 UDB can keep track of state of a row before and after the statement that fired the trigger. In the Correlation name for the old rows area, specify
OLDROW and in the Correlation name for the new rows area, specify
Figure 7. Specify NEWROW
Note that you might not be able to specify a correlation name for an option depending on the combination of activation time and the particular operation that causes the trigger to fire. For example, let's say you chose a Time to trigger action of Before for an trigger that is fired by a DELETE statement. In such a scenario, we won't be able to specify a "Correlation name for the new rows" Why? Because after a delete, a new row will not exist.
Because you are formulating a before trigger invoked by an UPDATE, you cannot populate the Temporary table for the old rows and Temporary table for the new rows options.
You'll notice that in this case (a before trigger invoked by an update), you can only specify that the trigger fire on each row, not each statement.
Figure 8. Trigger fires on Row
The statement that causes a triggering event might affect multiple rows in the database. The "For each Row" option means that the trigger will be activated once for each row that is modified. On the other hand, the "For each statement" (not allowed for "before" triggers) would mean that the actions defined by a trigger would only be performed once for the invoking SQL statement.
You can click the Show SQL button to see what the underlying SQL statement looks like so far:
Figure 9. Show SQL box
Now it's time to build the triggered action. Our business rule states that a checking balance must fall below zero for the trigger to be invoked. Namely, we want to specify the search-condition of
NEWROW.CHECKINGBALANCE<0. We specify
NEWROW.CHECKINGBALANCE because we want to analyze the checking balance as it would be after the update occurred.
We are now going to replace the
triggered-SQL-statement (see below).
Figure 10. Create the trigger statement
in the Triggered Action text area with:
declare overage decimal (7,2); set overage = (NEWROW.CHECKINGBALANCE*-1); if overage>OLDROW.SAVINGBALANCE then SIGNAL SQLSTATE '70001' ('Overdraft Protection Unsuccessful'); else set newrow.savingbalance = oldrow.savingbalance-overage, newrow.checkingbalance = 0; end if;
Let's dissect this logic piece by piece. In a trigger body, you can declare variables that you want to use in the body of the trigger. We do this with the line:
decimal(7,2)declare overage decimal (7,2); where we declared a variable named overage of the type
Next we set the value of our overage variable to the value of
We'll use this calculation to figure out how much extra (the overage) we are trying to pull out from checking account.
NEWROW.CHECKINGBALANCE is specified because we want to analyze the checking balance as it would be if the update were carried out.
set overage = (NEWROW.CHECKINGBALANCE*-1);
You can use a SIGNAL statement to raise an error condition if the business rule defined in your trigger is violated. In our case, we do not allow someone to have a negative checking account balance. If someone tries to update the checking account balance column to a negative amount, we try and see if we have enough in the savings account to make up for the negative amount. If not, we signal the SQL state of '70001' with the message of "'Overdraft Protection Unsuccessful".
It is important to realize the impact of the inclusion of our SIGNAL statement. The SIGNAL statement rolls back the changes that were attempted by the triggering statement (that is, our update statement). SIGNAL statements will also roll back changes caused within the trigger. Furthermore, let us say that we are using something like a JavaTM application that interacts with our database and tries to perform an update operation that fires our trigger and violates our business rule. The Java application will receive the SQLSTATE we specified and the SQLCODE of -438. We use the SIGNAL SQLSTATE feature in this line:
if overage>OLDROW.SAVINGBALANCE then SIGNAL SQLSTATE '70001' ('Overdraft Protection Unsuccessful');
which states that if our overage is greater than the amount in our savings balance, then we need to go ahead and raise a red flag.
Transferring money occurs if the savings account balance is large enough to make up for the overage. If this condition is met, we perform two modifications to the new row:
- Modify the savingbalance column of the "new row" and subtract the overage to facilitate the overdraft transfer.
- Set the new row's checking balance to zero. We do this with our code of:
else set newrow.savingbalance = oldrow.savingbalance-overage, newrow.checkingbalance = 0; end if;
You can see the final product of your efforts by using the Show SQL button again:
Figure 11. Show SQL
When you click Close, you should see that the OVERDRAFT trigger has been created:
Figure 12. Create OVERDRAFT trigger
Test things out by issuing an update statement. Go ahead and issue the following statement:
db2=> update accttable set checkingbalance = -500 where ssn='111-11-1111'
Following the business logic we created, this update should fire our trigger which will take 500.00 from the savingbalance column to account for the overdraft of the checking account. Consequently, our checkingbalance will be 0.00 and our savingbalance will be 1000.00 (the original balance of 1500 - 500 of overdraft) for the account with SSN 111-11-1111. The query shown below verifies this:
Figure 13. Query
You've created a DB2 trigger in the context of a mock business scenario. Triggers are a powerful DB2 database feature that can be used to polarize business logic to the relational database. Such a polarization is quite powerful considering the fact that you might have multiple applications trying to interact with the same database. Many times, in a large enterprise, you might even be unaware of what applications are being built that will interact with your database. Rather than just hope that these applications follow business rules that are considered the commandments of your organization (that is, they are deemed always true) , you can use triggers as one of the tools in your toolbox to make sure such business rules are enforced by all applications that interact with your database now and in the future.
A trigger can only be associated with a table and not a view. You might consider using INSTEAD OF triggers for interaction with views. These were added DB2 UDB in version 8.1. You can learn more about these INSTEAD OF triggers in Serge Rielaus's DB2 Developer Domain article
Triggers can also be used to invoke UDFs. Such a setup is quite powerful when you would like to perform an operation such as send an e-mail to a person as a result of a certain change in your database. I actually cover this in an earlier DB2 Developer Domain article.
Kulvir Singh Bhogal works as an IBM consultant, devising and implementing Java-centric solutions at customer sites across the nation. You can reach Kulvir at firstname.lastname@example.org.