Tải bản đầy đủ (.pdf) (108 trang)

Beginning PHP and MySQL From Novice to Professional phần 9 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.48 MB, 108 trang )


830
CHAPTER 32
■ STORED ROUTINES
You may be wondering about the DELIMITER statement, though. By default, MySQL
uses the semicolon to determine when a statement has concluded. However, when
creating a multistatement stored routine, you need to write several statements, but
you don’t want MySQL to do anything until you’ve finished writing the stored routine.
Therefore, you must change the delimiter to another character string. It doesn’t have
to be
//. You can choose whatever you please, ||| or ^^, for instance.
Executing a Stored Routine
Executing a stored routine is accomplished by referencing the stored routine in
conjunction with the
CALL statement. For example, executing the previously created
get_inventory procedure is accomplished like so:
mysql>CALL get_inventory(@inv);
mysql>SELECT @inv;
Executing get_inventory will return:
+ +
| @inv |
+ +
| 45 |
+ +
Creating and Using Multistatement Stored Routines
Single-statement stored routines are quite useful, but stored routines’ real power lies
in their ability to encapsulate and execute several statements. In fact, an entire language
is at your disposal, enabling you to perform rather complex tasks such as conditional
evaluation and iteration. For instance, suppose your company’s revenues are driven
by a sales staff. To coax the staff into meeting its lofty goals, bonuses are tacked onto
their monthly paychecks, with the size of the bonus proportional to the revenues


attributed to the employee. The company handles its payroll internally, using a custom
Java program to calculate and print the bonus checks at the conclusion of each year;
Gilmore_862-8C32.fm Page 830 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
831
however, a Web-based interface is provided to the sales staff so that it can monitor its
progress (and bonus size) in real time. Because both applications would require the
ability to calculate the bonus amount, this task seems like an ideal candidate for a
stored function. The syntax for creating this stored function looks like this:
DELIMITER //
CREATE FUNCTION calculate_bonus
(emp_id CHAR(8)) RETURNS DECIMAL(10,2)
COMMENT 'Calculate employee bonus'
BEGIN
DECLARE total DECIMAL(10,2);
DECLARE bonus DECIMAL(10,2);
SELECT SUM(revenue) INTO total FROM sales WHERE employee_id = emp_id;
SET bonus = total * .05;
RETURN bonus;
END;
//
DELIMITER ;
The calculate_bonus function would then be called like this:
mysql>SELECT calculate_bonus("35558ZHU");
This function returns something similar to this:
+ +
| calculate_bonus("35558ZHU") |
+ +
| 295.02 |
+ +

Even though this example includes some new syntax (all of which will soon be
introduced), it should be rather straightforward.
The remainder of this section is devoted to coverage of the syntax commonly used
when creating multistatement stored routines.
Gilmore_862-8C32.fm Page 831 Friday, February 15, 2008 7:34 AM
832
CHAPTER 32
■ STORED ROUTINES
EFFECTIVE STORED ROUTINE MANAGEMENT
Stored routines can quickly become lengthy and complex, adding to the time required to create and
debug their syntax. For instance, typing in the
calculate_bonus procedure can be tedious, partic-
ularly if along the way you introduced a syntax error that required the entire routine to be entered
anew. To alleviate some of the tedium, insert the stored routine creation syntax into a text file, and
then read that file into the
mysql client, like so:
%>mysql [options] < calculate_bonus.sql
The [options] string is a placeholder for your connection variables. Don’t forget to change
over to the appropriate database before creating the routine, by adding
USE db_name; to the top
of the script; otherwise, an error will occur.
To modify an existing routine, you can change the file as necessary, delete the existing routine
by using
DROP PROCEDURE (introduced later in this chapter), and then re-create it using the above
process. While there is an
ALTER PROCEDURE statement (also introduced later in this chapter), it is
presently only capable of modifying routine characteristics.
Another very effective mechanism for managing routines is through MySQL Query Browser,
introduced in Chapter 27. Via the interface you can create, edit, and delete routines.
The BEGIN and END Block

When creating multistatement stored routines, you need to enclose the statements in
a
BEGIN/END block. The block prototype looks like this:
BEGIN
statement 1;
statement 2;

statement N;
END
Note that each statement in the block must end with a semicolon.
Conditionals
Basing task execution on run-time information is key for wielding tight control over
its outcome. Stored routine syntax offers two well-known constructs for performing
conditional evaluation: the
IF-ELSEIF-ELSE statement and the CASE statement. Both
are introduced in this section.
Gilmore_862-8C32.fm Page 832 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
833
IF-ELSEIF-ELSE
The
IF-ELSEIF-ELSE statement is one of the most common means for evaluating
conditional statements. In fact, even if you’re a novice programmer, you’ve likely
already used it on numerous occasions. Therefore, this introduction should be quite
familiar. The prototype looks like this:
IF condition THEN statement_list
[ELSEIF condition THEN statement_list] . . .
[ELSE statement_list]
END IF
For example, suppose you modified the previously created calculate_bonus stored

procedure to determine the bonus percentage based on not only sales but also the
number of years the salesperson has been employed at the company:
IF years_employed < 5 THEN
SET bonus = total * .05;
ELSEIF years_employed >= 5 and years_employed < 10 THEN
SET bonus = total * .06;
ELSEIF years_employed >=10 THEN
SET bonus = total * .07;
END IF
CASE
The
CASE statement is useful when you need to compare a value against an array of
possibilities. While doing so is certainly possible using an
IF statement, the code
readability improves considerably by using the
CASE statement. Its prototype looks
like this:
CASE
WHEN condition THEN statement_list
[WHEN condition THEN statement_list] . . .
[ELSE statement_list]
END CASE
Consider the following example, which sets a variable containing the appropriate
sales tax rate by comparing a customer’s state to a list of values:
Gilmore_862-8C32.fm Page 833 Friday, February 15, 2008 7:34 AM
834
CHAPTER 32
■ STORED ROUTINES
CASE
WHEN state="AL" THEN:

SET tax_rate = .04;
WHEN state="AK" THEN:
SET tax_rate = .00;

WHEN state="WY" THEN:
SET tax_rate = .04;
END CASE;
Alternatively, you can save some typing by using the following variation:
CASE state
WHEN "AL" THEN:
SET tax_rate = .04;
WHEN "AK" THEN:
SET tax_rate = .00;

WHEN "WY" THEN:
SET tax_rate = .04;
END CASE;
Iteration
Some tasks, such as inserting a number of new rows into a table, require the ability
to repeatedly execute over a set of statements. This section introduces the various
methods available for iterating and exiting loops.
ITERATE
Executing the
ITERATE statement causes the LOOP, REPEAT, or WHILE block within which
it’s embedded to return to the top and execute again. Its prototype looks like this:
ITERATE label
Consider an example. The following stored procedure will increase every employee’s
salary by 5 percent, except for those assigned the employee category of 0:
DELIMITER //
DROP PROCEDURE IF EXISTS `corporate`.`calc_bonus`//

Gilmore_862-8C32.fm Page 834 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
835
CREATE PROCEDURE `corporate`.`calc_bonus` ()
BEGIN
DECLARE empID INT;
DECLARE emp_cat INT;
DECLARE sal DECIMAL(8,2);
DECLARE finished INTEGER DEFAULT 0;
DECLARE emp_cur CURSOR FOR
SELECT employee_id, salary FROM employees ORDER BY employee_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished=1;
OPEN emp_cur;
calcloop: LOOP
FETCH emp_cur INTO empID, emp_cat, sal;
IF finished=1 THEN
LEAVE calcloop;
END IF;
IF emp_cat=0 THEN
ITERATE calcloop;
END IF;
UPDATE employees SET salary = sal + sal * 0.05 WHERE employee_id=empID;
END LOOP calcloop;
CLOSE emp_cur;
END//
DELIMITER ;
You might have noticed that in this example a cursor was used to iterate through
each row of the result set. If you’re not familiar with this feature, see Chapter 35.
Gilmore_862-8C32.fm Page 835 Friday, February 15, 2008 7:34 AM
836

CHAPTER 32
■ STORED ROUTINES
LEAVE
Pending the value of a variable or outcome of a particular task, you may want to
immediately exit a loop or a
BEGIN/END block by using the LEAVE command. Its proto-
type follows:
LEAVE label
An example of LEAVE in action is provided in the introduction to LOOP, next. You’ll
also find an example in the previous example.
LOOP
The
LOOP statement will continue iterating over a set of statements defined in its block
until the
LEAVE statement is encountered. Its prototype follows:
[begin_label:] LOOP
statement_list
END LOOP [end_label]
MySQL stored routines are unable to accept arrays as input parameters, but you
can mimic the behavior by passing in and parsing a delimited string. For example,
suppose you provide clients with an interface for choosing among an array of ten
corporate services they’d like to learn more about. The interface might be presented
as a multiple-select box, checkboxes, or some other mechanism; which one you use
is not important, because ultimately the array of values would be condensed into a
string (using PHP’s
implode() function, for instance) before being passed to the stored
routine. For instance, the string might look like this, with each number representing
the numerical identifier of a desired service:
1,3,4,7,8,9,10
The stored procedure created to parse this string and insert the values into the

database might look like this:
DELIMITER //
CREATE PROCEDURE service_info
(client_id INT, services varchar(20))
BEGIN
Gilmore_862-8C32.fm Page 836 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
837
DECLARE comma_pos INT;
DECLARE current_id INT;
svcs: LOOP

SET comma_pos = LOCATE(',', services);
SET current_id = SUBSTR(services, 1, comma_pos);
IF current_id <> 0 THEN
SET services = SUBSTR(services, comma_pos+1);
ELSE
SET current_id = services;
END IF;
INSERT INTO request_info VALUES(NULL, client_id, current_id);
IF comma_pos = 0 OR current_id = '' THEN
LEAVE svcs;
END IF;
END LOOP;
END//
DELIMITER ;
Now call service_info, like so:
call service_info("45","1,4,6");
Once executed, the request_info table will contain the following three rows:
+ + + +

| row_id | client_id | service |
+ + + +
| 1 | 45 | 1 |
| 2 | 45 | 4 |
| 3 | 45 | 6 |
+ + + +
Gilmore_862-8C32.fm Page 837 Friday, February 15, 2008 7:34 AM
838
CHAPTER 32
■ STORED ROUTINES
REPEAT
The
REPEAT statement operates almost identically to WHILE, looping over a designated
statement or set of statements for as long as a certain condition is true. However,
unlike
WHILE, REPEAT evaluates the conditional after each iteration rather than before,
making it akin to PHP’s
DO…WHILE construct. Its prototype follows:
[begin_label:] REPEAT
statement_list
UNTIL condition
END REPEAT [end_label]
For example, suppose you were testing a new set of applications and wanted to
build a stored procedure that would fill a table with a given number of test rows. The
procedure follows:
DELIMITER //
CREATE PROCEDURE test_data
(rows INT)
BEGIN
DECLARE val1 FLOAT;

DECLARE val2 FLOAT;
REPEAT
SELECT RAND() INTO val1;
SELECT RAND() INTO val2;
INSERT INTO analysis VALUES(NULL, val1, val2);
SET rows = rows - 1;
UNTIL rows = 0
END REPEAT;
END//
DELIMITER ;
Executing this procedure passing in a rows parameter of 5 produces the following
result:
Gilmore_862-8C32.fm Page 838 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
839
+ + + +
| row_id | val1 | val2 |
+ + + +
| 1 | 0.0632789 | 0.980422 |
| 2 | 0.712274 | 0.620106 |
| 3 | 0.963705 | 0.958209 |
| 4 | 0.899929 | 0.625017 |
| 5 | 0.425301 | 0.251453 |
+ + + +
WHILE
The
WHILE statement is common among many, if not all, modern programming
languages, iterating one or several statements for as long as a particular condition or
set of conditions remains true. Its prototype follows:
[begin_label:] WHILE condition DO

statement_list
END WHILE [end_label]
The test_data procedure first created in the above introduction to REPEAT has been
rewritten, this time using a
WHILE loop:
DELIMITER //
CREATE PROCEDURE test_data
(rows INT)
BEGIN
DECLARE val1 FLOAT;
DECLARE val2 FLOAT;
WHILE rows > 0 DO
SELECT RAND() INTO val1;
SELECT RAND() INTO val2;
INSERT INTO analysis VALUES(NULL, val1, val2);
SET rows = rows - 1;
END WHILE;
END//
DELIMITER ;
Gilmore_862-8C32.fm Page 839 Friday, February 15, 2008 7:34 AM
840
CHAPTER 32
■ STORED ROUTINES
Executing this procedure produces similar results to those shown in the REPEAT
section.
Calling a Routine from Within Another Routine
It’s possible to call a routine from within another routine, saving you the inconve-
nience of having to repeat logic unnecessarily. An example follows:
DELIMITER //
CREATE PROCEDURE process_logs()

BEGIN
SELECT "Processing Logs";
END//
CREATE PROCEDURE process_users()
BEGIN
SELECT "Processing Users";
END//
CREATE PROCEDURE maintenance()
BEGIN
CALL process_logs();
CALL process_users();
END//
DELIMITER ;
Executing the maintenance() procedure produces the following:
+ +
| Processing Logs |
+ +
| Processing Logs |
+ +
1 row in set (0.00 sec)
Gilmore_862-8C32.fm Page 840 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
841
+ +
| Processing Users |
+ +
| Processing Users |
+ +
1 row in set (0.00 sec)
Modifying a Stored Routine

At present MySQL only offers the ability to modify stored routine characteristics, via
the
ALTER statement. Its prototype follows:
ALTER (PROCEDURE | FUNCTION) routine_name [characteristic ]
For example, suppose you want to change the SQL SECURITY characteristic of the
calculate_bonus method from the default of DEFINER to INVOKER:
ALTER PROCEDURE calculate_bonus SQL SECURITY invoker;
Deleting a Stored Routine
To delete a stored routine, execute the DROP statement. Its prototype follows:
DROP (PROCEDURE | FUNCTION) [IF EXISTS] sp_name
For example, to drop the calculate_bonus stored procedure, execute the following
command:
mysql>DROP PROCEDURE calculate_bonus;
As of version 5.0.3, you’ll need the ALTER ROUTINE privilege to execute DROP.
Viewing a Routine’s Status
On occasion you may be interested to learn more about who created a particular
routine, the routine’s creation or modification time, or to what database the routine
applies. This is easily accomplished with the
SHOW STATUS statement. Its prototype
looks like this:
SHOW (PROCEDURE | FUNCTION) STATUS [LIKE 'pattern']
Gilmore_862-8C32.fm Page 841 Friday, February 15, 2008 7:34 AM
842
CHAPTER 32
■ STORED ROUTINES
For example, suppose you want to learn more about a previously created
get_products() stored procedure:
mysql>SHOW PROCEDURE STATUS LIKE 'get_products'\G
Executing this command produces the following output:
*************************** 1. row ***************************

Db: corporate
Name: get_products
Type: PROCEDURE
Definer: root@localhost
Modified: 2008-03-12 19:07:34
Created: 2008-03-12 19:07:34
Security_type: DEFINER
Comment:
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci
1 row in set (0.01 sec)
Note that the \G option was used to display the output in vertical rather than hori-
zontal format. Neglecting to include
\G produces the results horizontally, which can
be difficult to read.
It’s also possible to use a wildcard if you want to view information regarding
several stored routines simultaneously. For instance, suppose another stored routine
named
get_employees() was available:
mysql>SHOW PROCEDURE STATUS LIKE 'get_%'\G
This would produce:
Gilmore_862-8C32.fm Page 842 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
843
*************************** 1. row ***************************
Db: corporate
Name: get_employees
Type: PROCEDURE
Definer: jason@localhost

Modified: 2008-03-12 23:05:28
Created: 2008-03-12 23:05:28
Security_type: DEFINER
Comment:
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci
*************************** 2. row ***************************
Db: corporate
Name: get_products
Type: PROCEDURE
Definer: root@localhost
Modified: 2008-03-12 19:07:34
Created: 2008-03-12 19:07:34
Security_type: DEFINER
Comment:
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci
2 rows in set (0.02 sec)
Viewing a Routine’s Creation Syntax
It’s possible to review the syntax used to create a particular routine, by using the SHOW
CREATE
statement. Its prototype follows:
SHOW CREATE (PROCEDURE | FUNCTION) dbname.spname
Gilmore_862-8C32.fm Page 843 Friday, February 15, 2008 7:34 AM
844
CHAPTER 32
■ STORED ROUTINES
For example, the following statement will re-create the syntax used to create the

get_products() procedure:
SHOW CREATE PROCEDURE corporate.maintenance\G
Executing this command produces the following output (slightly formatted for
readability):
*************************** 1. row ***************************
Procedure: maintenance
sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `maintenance`()
BEGIN
CALL process_logs();
CALL process_users();
END
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci
Handling Conditions
Earlier, this chapter mentioned that the DECLARE statement can also specify handlers
that can execute should a particular situation, or condition, occur. For instance, earlier in
this chapter a handler was used in the
calc_bonus procedure to determine when the
iteration of a result set had completed. Two declarations were required, a variable
named
finished and a handler for the NOT FOUND condition:
DECLARE finished INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished=1;
Once the iteration loop was entered, finished was checked with each iteration, and
if it was set to 1, the loop would be exited:
IF finished=1 THEN
LEAVE calcloop;
END IF;

Gilmore_862-8C32.fm Page 844 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES
845
MySQL supports numerous conditions that can be reacted to as necessary. See the
MySQL documentation for more details.
Integrating Routines into Web Applications
Thus far, all the examples have been demonstrated by way of the MySQL client. While
this is certainly an efficient means for testing examples, the utility of stored routines
is drastically increased by the ability to incorporate them into your application. This
section demonstrates just how easy it is to integrate stored routines into your PHP-
driven Web application.
Creating the Employee Bonus Interface
Returning to the multistatement stored function example involving the calculation of
employee bonuses, it was mentioned that a Web-based interface was offered to
enable employees to track their yearly bonus in real time. This example demonstrates
just how easily this is accomplished using the
calculate_bonus() stored function.
Listing 32-1 presents the simple HTML form used to prompt for the employee ID.
Of course, in a real-world situation, such a form would also request a password; however,
for the purposes of this example an ID is sufficient.
Listing 32-1. The Employee Login Form (login.php)
<form action="viewbonus.php" method="post">
Employee ID:<br />
<input type="text" name="employeeid" size="8" maxlength="8" value="" />
<input type="submit" value="View Present Bonus" />
</form>
Listing 32-2 receives the information provided by login.php, using the provided
employee ID and
calculate_bonus() stored function to calculate and display the
bonus information.

Gilmore_862-8C32.fm Page 845 Friday, February 15, 2008 7:34 AM
846
CHAPTER 32
■ STORED ROUTINES
Listing 32-2. Retrieving the Present Bonus Amount (viewbonus.php)
<?php
// Instantiate the mysqli class
$db = new mysqli("localhost", "websiteuser", "jason", "corporate");
// Assign the employeeID
$eid = htmlentities($_POST['employeeid']);
// Execute the stored procedure
$result = $db->query("SELECT calculate_bonus('$eid')");
$row = $result->fetch_row();
printf("Your bonus is \$%01.2f",$row[0]);
?>
Executing this example produces output similar to this:
Your bonus is $295.02
Retrieving Multiple Rows
Although the above example should suffice for understanding how multiple rows are
returned from a stored routine, the following brief example makes it abundantly
clear. Suppose you create a stored procedure that retrieves information regarding
company employees:
CREATE PROCEDURE get_employees()
SELECT employee_id, name, position FROM employees ORDER by name;
This procedure can then be called from within a PHP script like so:
<?php
// Instantiate the mysqli class
$db = new mysqli("localhost", "root", "jason", "corporate");
Gilmore_862-8C32.fm Page 846 Friday, February 15, 2008 7:34 AM
CHAPTER 32 ■ STORED ROUTINES

847
// Execute the stored procedure
$result = $db->query("CALL get_employees()");
// Loop through the results
while (list($employee_id, $name, $position) = $result->fetch_row()) {
echo "$employee_id, $name, $position <br />";
}
?>
Executing this script produces output similar to the following:
EMP12388, Clint Eastwood, Director
EMP76777, John Wayne, Actor
EMP87824, Miles Davis, Musician
Summary
This chapter introduced stored routines. You learned about the advantages and disad-
vantages to consider when determining whether this feature should be incorporated into
your development strategy, and all about MySQL’s specific implementation and syntax.
Finally, you learned how easy it is to incorporate both stored functions and stored proce-
dures into your PHP applications.
The next chapter introduces another feature new to MySQL 5: triggers.
Gilmore_862-8C32.fm Page 847 Friday, February 15, 2008 7:34 AM
Gilmore_862-8C32.fm Page 848 Friday, February 15, 2008 7:34 AM
849
■ ■ ■
CHAPTER 33
MySQL Triggers
A trigger is a task that executes in response to some predetermined event. Specifi-
cally, 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 incorpo-
rate trigger features into your PHP-driven Web applications.
Introducing Triggers
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 chal-
lenge has to do with managing data, which includes tasks such as the following:
• Preventing corruption due to malformed data
• Enforcing business rules, such as ensuring that an attempt to insert information
about a product into the product table includes the identifier of a manufacturer
whose information already resides in the manufacturer table
• Ensuring database integrity by cascading changes throughout a database, such
as removing all products whose manufacturer ID matches one you’d like to
remove from the system
If 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
Gilmore_862-8C33.fm Page 849 Wednesday, February 27, 2008 9:04 AM
850
CHAPTER 33
■ MYSQL TRIGGERS
is interacting with the database. Database triggers give you that choice, which is why they
are considered indispensable by many developers.
Why Use Triggers?
You might consider using triggers for any of the following purposes:
• Audit trails: Suppose you are using MySQL to log Apache traffic (say, using the
Apache 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 tabu-
late and display the results to an impatient executive. Executing this additional

insertion can be done automatically with a trigger.
• Validation: You can use triggers to validate data before updating the database,
such as to ensure that a minimum-order threshold has been met.
• Referential integrity enforcement: Sound database administration practice
dictates that table relationships remain stable throughout the lifetime of a
project. Rather than attempt to incorporate all integrity constraints program-
matically, it occasionally may make sense to use triggers to ensure that these
tasks occur automatically.
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.
Taking Action Before 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
Gilmore_862-8C33.fm Page 850 Wednesday, February 27, 2008 9:04 AM
CHAPTER 33 ■ MYSQL TRIGGERS
851
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
Taking Action After an Event
Most helpdesk support software is based upon the notion of ticket assignment and reso-
lution. 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 | | 1 |
| 2 | Robert | | 1 |
| 3 | Matt | | 1 |
+ + + + +
The tickets table looks like this:
Gilmore_862-8C33.fm Page 851 Wednesday, February 27, 2008 9:04 AM
852
CHAPTER 33
■ MYSQL TRIGGERS
+ + + + + +
| 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.
Before Triggers vs. After Triggers
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.
Gilmore_862-8C33.fm Page 852 Wednesday, February 27, 2008 9:04 AM
CHAPTER 33 ■ MYSQL TRIGGERS

853
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’s Trigger Support
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.
• Views are not supported: A trigger can’t be used in conjunction with a view.
• Result sets can’t be returned from a trigger: It’s only possible to execute INSERT,
UPDATE, and DELETE queries within a trigger. You can also execute stored routines
within a trigger, provided they don’t return result sets, as well as the SET
command.
• Transactions are not supported: A trigger can’t be involved in the beginning or
conclusion of a transaction (namely, START TRANSACTION, COMMIT, and ROLLBACK
statements cannot be used within a transaction).
• Triggers must be unique: It’s not possible to create multiple triggers sharing
the same table, event (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.
• Error handling and reporting support is immature: Although, as expected,
MySQL will prevent an operation from being performed if a before or after
trigger fails, there is presently no graceful way to cause the trigger to fail and
return useful information to the user.
While such limitations may leave you scratching your head regarding the practi-
cality 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 possibil-

ities for taking advantage of this important new feature. Read on to learn how you can
Gilmore_862-8C33.fm Page 853 Wednesday, February 27, 2008 9:04 AM

×