A trigger is a task that executes in response to some predetermined event. Specifically, this event involves inserting, modifying, or deleting table data, and the task can occur either prior to or immediately following any such event. This chapter introduces triggers, a feature available as of MySQL 5.0.2. This chapter begins by offering general examples that illustrate how you can use triggers to carry out tasks such as enforcing referential integrity and business rules, gathering statistics, and preventing invalid transactions. This chapter then discusses MySQL's trigger implementation, showing you how to create, execute, and manage triggers. Finally, you'll learn how to incorporate trigger features into your PHP-driven Web applications.
As developers, we have to remember to implement an extraordinary number of details in order for an application to operate properly. Of course, much of the challenge has to do with managing data, which includes tasks such as the following:
product
table includes the identifier of a manufacturer whose information already resides in the manufacturer
tableIf you've built even a simple application, you've likely spent some time writing code to carry out at least some of these tasks. Given the choice, you'd probably rather have some of these tasks carried out automatically on the server side, regardless of which application is interacting with the database. Database triggers give you that choice, which is why they are considered indispensable by many developers.
You might consider using triggers for any of the following purposes:
mod_log_sql
module) but you also want to create an additional special logging table that tracks just site zone traffic and enables you to quickly tabulate and display the results to an impatient executive. Executing this additional insertion can be done automatically with a trigger.The utility of triggers stretches far beyond these purposes. Suppose you want to update the corporate Web site when the $1 million monthly revenue target is met. Or suppose you want to e-mail any employee who misses more than two days of work in a week. Or perhaps you want to notify a manufacturer if inventory runs low on a particular product. All of these tasks can be handled by triggers.
To provide you with a better idea of the utility of triggers, let's consider two scenarios, the first involving a before trigger, a trigger that occurs prior to an event, and the second involving an after trigger, a trigger that occurs after an event.
Suppose that a gourmet-food distributor requires that at least $10 of coffee be purchased before it will process the transaction. If a user attempts to add less than this amount to the shopping cart, that value will automatically be rounded up to $10. This process is easily accomplished with a before trigger, which, in this example, evaluates any attempt to insert a product into a shopping cart, and increases any unacceptably low coffee purchase sum to $10. The general process looks like this:
Shopping cart insertion request submitted:
If product identifier set to "coffee":
If dollar amount < $10:
Set dollar amount = $10;
End If
End If
Process insertion request
Most helpdesk support software is based upon the notion of ticket assignment and resolution. Tickets are both assigned to and resolved by helpdesk technicians, who are responsible for logging ticket information. However, occasionally even the technicians are allowed out of their cubicle, sometimes even for a brief vacation or because they are ill. Clients can't be expected to wait for the technician to return during such absences, so the technician's tickets should be placed back in the pool for reassignment by the manager. This process should be automatic so that outstanding tickets aren't potentially ignored. Therefore, it makes sense to use a trigger to ensure that the matter is never overlooked.
For purposes of example, assume that the technicians
table looks like this:
+--------+---------+--------------------------+------------+
| id | name | email | available |
+--------+---------+--------------------------+------------+
| 1 | Jason | [email protected] | 1 |
| 2 | Robert | [email protected] | 1 |
| 3 | Matt | [email protected] | 1 |
+--------+---------+--------------------------+------------+
The tickets
table looks like this:
+------+-----------+-----------------+---------------------+----------------+
| id | username | title | description | technician_id |
+------+-----------+-----------------+---------------------+----------------+
| 1 | smith22 | disk drive | Disk stuck in drive | 1 |
| 2 | gilroy4 | broken keyboard | Enter key is stuck | 1 |
| 3 | cornell15 | login problems | Forgot password | 3 |
| 4 | mills443 | login problems | forgot username | 2 |
+------+-----------+-----------------+---------------------+----------------+
Therefore, to designate a technician as out-of-office, the available
flag needs to be set accordingly (0
for out-of-office, 1
for in-office) in the technicians
table. If a query is executed setting that column to 0
for a given technician, his tickets should all be placed back in the general pool for eventual reassignment. The after trigger process looks like this:
Technician table update request submitted:
If available column set to 0:
Update tickets table, setting any flag assigned
to the technician back to the general pool.
End If
Later in this chapter, you'll learn how to implement this trigger and incorporate it into a Web application.
You may be wondering how one arrives at the conclusion to use a before trigger in lieu of an after trigger. For example, in the after trigger scenario in the previous section, why couldn't the ticket reassignment take place prior to the change to the technician's availability status? Standard practice dictates that you should use a before trigger when validating or modifying data that you intend to insert or update. A before trigger shouldn't be used to enforce propagation or referential integrity, because it's possible that other before triggers could execute after it, meaning the executing trigger may be working with soon-to-be-invalid data.
On the other hand, an after trigger should be used when data is to be propagated or verified against other tables, and for carrying out calculations, because you can be sure the trigger is working with the final version of the data.
In the following sections, you'll learn how to create, manage, and execute MySQL triggers most effectively. Numerous examples involving trigger usage in PHP/MySQL-driven applications are also presented.
MySQL supports triggers as of version 5.0.2, but at the time of writing, this new feature was still under heavy development. While the previous introductory examples demonstrate what's already possible, there are still several limitations. For instance, as of version 5.1.21 beta, the following deficiencies exist:
TEMPORARY
tables are not supported: A trigger can't be used in conjunction with a TEMPORARY
table.SET
command.START TRANSACTION
, COMMIT
, and ROLLBACK
statements cannot be used within a transaction).INSERT
, UPDATE
, DELETE
), and cue (before, after). However, because multiple commands can be executed within the boundaries of a single query (as you'll soon learn), this shouldn't really present a problem.While such limitations may leave you scratching your head regarding the practicality of using triggers at this stage, keep in mind that this is very much a work in progress. That said, even at this early developmental stage, there are several possibilities for taking advantage of this important new feature. Read on to learn how you can begin incorporating triggers into your MySQL databases, beginning with an introduction to their creation.
MySQL triggers are created using a rather straightforward SQL statement. The syntax prototype follows:
CREATE
[DEFINER = { USER | CURRENT_USER }]
TRIGGER <trigger name>
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON <table name>
FOR EACH ROW
<triggered SQL statement>
As you can see, it's possible to specify whether the trigger should execute before or after the query, whether it should take place on row insertion, modification, or deletion, and to what table the trigger applies.
The DEFINER
clause determines which user account will be consulted to determine whether appropriate privileges are available to execute the queries defined within the trigger. If defined, you'll need to specify both the username and hostname using 'user@host'
syntax (for example, 'jason@localhost'
). If CURRENT_USER
is used (the default), then the privileges of whichever account has caused the trigger to execute will be consulted. Only users having the SUPER
privilege are able to assign DEFINER
to another user.
Tip If you're using a version of MySQL earlier than 5.1.6, you need the SUPER
privilege to create triggers; starting with 5.1.6, you can do so if your account is assigned the TRIGGER
privilege.
The following implements the helpdesk trigger first described earlier in this chapter:
DELIMITER //
CREATE TRIGGER au_reassign_ticket
AFTER UPDATE ON technicians
FOR EACH ROW
BEGIN
IF NEW.available = 0 THEN
UPDATE tickets SET technician_id=0 WHERE technician_id=NEW.id;
END IF;
END;//
Note You may be wondering about the au
prefix in the trigger title. See the sidebar "Trigger Naming Conventions" for more information about this and similar prefixes.
For each row affected by an update to the technicians
table, the trigger will update the tickets
table, setting tickets.technician_id
to 0
wherever the technician_id
value specified in the UPDATE
query exists. You know the query value is being used because the alias NEW
prefixes the column name. It's also possible to use a column's original value by prefixing it with the OLD
alias.
Once the trigger has been created, go ahead and test it by inserting a few rows into the tickets
table and executing an UPDATE
query that sets a technician's availability
column to 0
:
UPDATE technicians SET available=0 WHERE id =1;
Now check the tickets
table, and you'll see that both tickets that were assigned to Jason are assigned no longer.
As of MySQL version 5.0.10, it's possible to view existing triggers in one of two ways: by using the SHOW TRIGGERS
command or by using the information schema. Both solutions are introduced in this section.
The SHOW TRIGGERS Command
The SHOW TRIGGERS
command produces several attributes for a trigger or set of triggers. Its prototype follows:
SHOW TRIGGERS [FROM db_name] [LIKE expr]
Because the output has a tendency to spill over to the next row, making it difficult to read, it's useful to execute SHOW TRIGGERS
with the G
flag, like so:
mysql>SHOW TRIGGERSG
Assuming only the previously created au_reassign_ticket
trigger exists in the present database, the output will look like this:
*************************** 1. row ***************************
Trigger: au_reassign_ticket
Event: UPDATE
Table: technicians
Statement: begin
if NEW.available = 0 THEN
UPDATE tickets SET technician_id=0 WHERE technician_id=NEW.id;
END IF;
END
Timing: AFTER
Created: NULL
sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Definer: root@localhost
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci
1 row in set (0.00 sec)
As you can see, all of the necessary descriptors can be found. However, viewing trigger information using the INFORMATION_SCHEMA
database offers a vastly improved methodology. This solution is introduced next.
The INFORMATION_SCHEMA
Executing a SELECT
query against the TRIGGERS
table found in the INFORMATION_SCHEMA
database displays information about triggers. This database was first introduced in Chapter 28.
mysql>SELECT * FROM INFORMATION_SCHEMA.triggers
->WHERE trigger_name="au_reassign_ticket"G
Executing this query retrieves even more information than what was shown in the previous example:
*************************** 1. row ***************************
TRIGGER_CATALOG: NULL
TRIGGER_SCHEMA: chapter33
TRIGGER_NAME: au_reassign_ticket
EVENT_MANIPULATION: UPDATE
EVENT_OBJECT_CATALOG: NULL
EVENT_OBJECT_SCHEMA: chapter33
EVENT_OBJECT_TABLE: technicians
ACTION_ORDER: 0
ACTION_CONDITION: NULL
ACTION_STATEMENT: begin
if NEW.available = 0 THEN
UPDATE tickets SET technician_id=0 WHERE technician_id=NEW.id;
END IF;
END
ACTION_ORIENTATION: ROW
ACTION_TIMING: AFTER
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
ACTION_REFERENCE_OLD_ROW: OLD
ACTION_REFERENCE_NEW_ROW: NEW
CREATED: NULL
SQL_MODE: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
DEFINER: root@localhost
CHARACTER_SET_CLIENT: latin1
COLLATION_CONNECTION: latin1_swedish_ci
DATABASE_COLLATION: latin1_swedish_ci
Of course, the beauty of querying the INFORMATION_SCHEMA
database is that it's so much more flexible than using SHOW. For example, suppose you are managing numerous triggers and want to know which ones triggered after a statement:
SELECT trigger_name FROM INFORMATION_SCHEMA.triggers WHERE action_timing="AFTER"
Or perhaps you'd like to know which triggers were executed whenever the technicians
table was the target of an INSERT
, UPDATE
, or DELETE
query:
mysql>SELECT trigger_name FROM INFORMATION_SCHEMA.triggers WHERE
->event_object_table="technicians"
At the time of writing, there was no supported command or GUI application available for modifying an existing trigger. Therefore, perhaps the easiest strategy for modifying a trigger is to delete and subsequently re-create it.
It's conceivable, particularly during a development phase, that you'll want to delete a trigger, or remove it if the action is no longer needed. This is accomplished by using the DROP TRIGGER
statement, the prototype of which follows:
DROP TRIGGER [IF EXISTS] table_name.trigger_name
For example, to remove the au_reassign_ticket
trigger, execute the following command:
DROP TRIGGER technicians.au_reassign_ticket;
You need the TRIGGER
or SUPER
privilege to successfully execute DROP TRIGGER
.
Caution When a database or table is dropped, all corresponding triggers are also deleted.
Because triggers occur transparently, you really don't need to do anything special to integrate their operation into your Web applications. Nonetheless, it's worth offering an example demonstrating just how useful this feature can be in terms of both decreasing the amount of PHP code and further simplifying the application logic. In this section you'll learn how to implement the helpdesk application first depicted earlier, in the section "Taking Action After an Event."
To begin, if you haven't done so already, go ahead and create the two tables (technicians
and tickets
) depicted in the earlier section, and add a few appropriate rows to each, making sure that each tickets.technician_id
matches a valid technicians.technician_id
. Next, create the au_reassign_ticket
trigger as previously described.
Recapping the scenario, submitted helpdesk tickets are resolved by assigning each to a technician. If a technician is out of the office for an extended period of time, say due to a vacation or illness, he is expected to update his profile by changing his availability status. The profile manager interface looks similar to that shown in Figure 33-1.
Figure 33-1. The helpdesk account interface
When the technician makes any changes to this interface and submits the form, the code presented in Listing 33-1 is activated.
Listing 33-1. Updating the Technician Profile
<?php
// Connect to the MySQL database
$mysqli = new mysqli("localhost", "websiteuser", "secret", "helpdesk");
// Assign the POSTed values for convenience
$email = htmlentities($_POST['email']);
$available = htmlentities($_POST['available']);
// Create the UPDATE query
$query = "UPDATE technicians SET available='$available' WHERE email='$email'";
// Execute query and offer user output
if ($mysqli->query($query)) {
echo "<p>Thank you for updating your profile.</p>";
if ($available == 0) {
echo "<p>Your tickets will be reassigned to another technician.</p>";
}
} else {
echo "<p>There was a problem updating your profile.</p>";
}
?>
Once this code has been executed, return to the tickets
table and you'll see that the relevant tickets have been unassigned.
This chapter introduced triggers, a feature new to MySQL 5. Triggers can greatly reduce the amount of code you need to write solely for ensuring the referential integrity and business rules of your database. You learned about the different trigger types and the conditions under which they will execute. An introduction to MySQL's trigger implementation was offered, followed by coverage of how to integrate these triggers into your PHP applications.
The next chapter introduces views, yet another feature new to MySQL 5.
18.119.133.160