Vous êtes sur la page 1sur 19

Content Taken From Website :- http://forge.mysql.com/wiki/Triggers MySQL have recently released version 5.0.2-Alpha of the MySQL database.

With thi s release in addition to bug fixes to Stored Procedures comes the first implemen tation of Triggers within the database. Triggers are stored routines much like procedures and functions however they are attached to tables and are fired automatically when a set action is performed o n the data in that table. [edit]Getting Going To program triggers you will need to have either completed the stored procedures tutorials or be comfortable using ANSI standard procedural code. Triggers are o nly available in MySQL version 5.0.2 or above, so if you have been using a lower version to carry out the Stored Procedure tutorials you will need to download t he newer version. If a higher version is available we recommend you use as high a release as possible, this can be downloaded from the MySQL web site for free. Be aware that much of the functionality mentioned in these tutorials is only sup ported from version 5.0.10 so you will need this to work fully with triggers. In addition to MySQL we will be using a basic text editor, this is not necessary b ut will make our life a little easier. Unlike the procedure tutorials which were run under Windows XP the Trigger proce dures have been written and run using Mac OS X, but again they should run exactl y the same on any other operating system you may be using. One final word, if th e only copy of MySQL you have is a lower version than 5.0.2 then unfortunately y ou won t be able to try the examples yourself, but you can still learn all about t riggers from the tutorials. [edit]Conventions Finally a word on the conventions we will be using in the tutorials. Any code th at you can type in will be displayed in one of two ways //This is code seen for the first time. //Or code you have already seen but is being reference later Changes to programs previously created during the tutorial will be shown in bold //This is old code here //This is the line we want you to add You can download a zipped copy of all the sources used in each chapter using the menu. But after each section of code will be a link to the actual source code f ile that we used. We suggest you try typing in each section of code yourself as this can often help you understand what is actually going on, but if you can t get a particular program to work feel free to use our code. [edit]Trigger Types A trigger is a stored unit of code that is attached to a table within the databa se. A trigger cannot be called or used in a select statement in the same way a p rocedure or function can but are called automatically when an action takes place on the table it is associated with. There are 12 different types of triggers th at are possible and these can be split into 3 different groups, they all have th e same functionality and limitations but are called at different stages of a tab le action. Before we look at the 12 types we will look at the syntax for creating a trigger . CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt Use the CREATE TRIGGER keywords to tell MySQL that we are creating a trigger, th en name the trigger using standard MySQL naming conventions. Its best to kept th e name short but descriptive, its also a good idea to include a code or naming c

onvention which shows the type of trigger that is being created, we will look at this when we have discussed the different types. [edit] Trigger Time The trigger time is at what stage during the process takes place. This can be ei ther BEFORE or AFTER. If using before this means that the trigger code will be a ble to work with both the values currently stored in there original state and th e new values, more on this later. If you use after then this is working on the t able after it has been updated. This is important in an environment which uses t ransactions there may be situations where we don t want to fire the trigger until a commit has taken place. [edit] Trigger Event The trigger event is the event that happens on the table the causes the trigger code to fire. This can be one of the following 3 types. INSERT UPDATE DELETE If you want the trigger to fire when an insert on the table takes place the you need to use the INSERT keyword, if you want the trigger to fire when an update t akes place then use UPDATE and of course if you want the action to fire when a d elete takes place use the DELETE keyword. We can start to see the different types of triggers it s possible to create using a combination of different trigger events and times. So for example it s possible to have both a BEFORE INSERT and AFTER INSERT trigger type. It s these combination with the use of a keyword that we will look at in a second that creates the 12 different types of trigger we can use. [edit] TABLE NAME The table name is then specified using the ON table_name keywords. Of course rep lace the table_name with the name of the table you wish to apply the trigger. It may be a good point to tell you that it s only possible to have one trigger per t ype in MySQL so for example if we have a table called emps it would only be poss ible to have one BEFORE INSERT trigger against it. This is unlike some other dat abase implementations such as Oracle which allows multiple triggers for any give n trigger type. We will look at this in more detail in a moment but it s not a maj or restriction when compared to Oracle for example as triggers are rudimentary i n MySQL by MySQL s own admission. [edit] FOR EACH ROW The last part of the trigger definition before the actual code is the FOR EACH R OW keyword, this is the only part of the trigger specification which is not mand atory. If it is used then the trigger will fire once for all records that are be ing worked on. If it is not specified the trigger will fire once only regardless of the number of records being update. Finally we can implement the code which will be fired when the trigger is called . As with procedures and functions this can be a single line directly after the trigger specification or multiple lines of code contained within a BEGIN and END . The code can contain any valid stored procedure statements, this includes loop s IF, LOOP, CASE and as from version 5.0.10 SQL statements. [edit] Creating Triggers To create a trigger the code needs to be entered into the MySQL command line eit her by typing it in long hand or by using a source file. In this section we will

be creating triggers against the tables contained in the setup.myp script. If y ou have previously downloaded this script it may be worth downloading this again as there have been some minor amendments to the tables to allow better testing conditions for triggers. drop database if exists pers create database pers use pers create table emps(emp_id int NOT NULL, emp_name varchar(30), dept_id int, dept_name varchar(30), salary decimal(7,2), primary key(emp_id)) insert into emps (emp_id,emp_name,dept_id,salary) values (1, Roger ,1,2000.00),(2, John ,2,2500.00),(3, Alan ,1,2100.00) select * from emps create table dept (dept_id int NOT NULL, description varchar(30), primary key(dept_id)) insert into dept (dept_id,description) values (1, Information Technology ),(2, Sales ) select * from dept Download Setup.myp The first trigger will be a simple one line trigger to demonstrate the basics of trigger creation. Our clients are a group of parallel universe accountants and when ever they enter any names they do so in reverse order. We need to create a simple trigger so that on entry of names into our emps table the value is revers ed. You may be aware that there is already a function within MySQL to reverse a stri ng so we can use that, but we don t want to have to get our users to type this in as part of their entry process and nor do we want to write code in some other la nguage as our database can be updated from a number of different systems. So before we create the trigger there are a number of decisions we need to take. First what to call our trigger, I mentioned before that it s best to have a namin g structure for triggers, MySQL makes it slightly easier in the fact you can hav e only one trigger per type per table. Therefore the obvious choice is to use th e table name and then add what type of trigger it is. For example we might want to create a BEFORE UPDATE trigger which effects all rows, we could use the follo wing naming convention BU_tablename_FER This may seem a bit cryptic but it s simply B for Before U for update, the table n ame and FER to show it s FOR EACH ROW. So to extend the convention if we wanted an AFTER INSERT trigger it would be AI_tablename and so on. It s useful to have a name that we can find easily in the future and al so makes it easy to identify what a trigger is doing without having to look at t he code. Take for example if we had created 3 triggers and just given them seque ntial names trigger_1, trigger_2 and trigger_3 we would have to look at the code of each trigger to find out which was the one we needed. Using the naming conve ntion it s easy to identify which triggers relate to which tables and also it s easy

to write scripts to drop triggers when a table is dropped. So back to our trigger, we want to update each emp_name column of our emps table when a record is inserted. We also want to change the values so we need to fire the trigger before the action is completed. So using this information we can se e we want a BEFORE INSERT ON emps FOR EACH ROW trigger. The specification for th is would be as follows. CREATE TRIGGER bi_emps_fer BEFORE INSERT OF emps FOR EACH ROW... You can see that the naming convention has been used. Once the specification of the trigger is complete the trigger code is then added, in our case we want to r everse the emp_name column, however as previously mentioned it s not possible to u se references to tables in triggers. [edit] OLD and NEW Rather than updating the table by name we can use the OLD and NEW keywords to ac cess the data in the record we are dealing with. OLD is used for the values of t he column before the change was made and NEW holds the value of the column after the column has changed. Therefore in our program we can access the emp_name col umn using new.emp_name create trigger bi_emps_fer before insert on emps for each row set new.emp_name := reverse(new.emp_name); Downlaod Trigger1.myp This is a simple one line trigger so we do not need to enclose the code within a BEGIN and END. Unlike procedures or functions we cannot invoke triggers manuall y, they must be called as a result of the relevant table action. To fire the tri gger a record needs to be inserted into the relevant table. insert into emps (emp_id,emp_name) values (4, Dave ); Query OK, 1 row affected (0.03 sec) At this stage there is no visual clue that the trigger has actually fired. Selec ting against the table will show use if the trigger has fire and reversed the em p_name column. mysql> select * from emps where emp_id = 4; -------- ---------- --------- ----------- -------emp_id emp_name dept_id dept_name salary -------- ---------- --------- ----------- -------4 evaD NULL NULL NULL -------- ---------- --------- ----------- -------1 row in set (0.02 sec) This shows us that the column was indeed reversed so the trigger must have fired . This was a relatively simple and contrived example of a trigger, in the next s ection we will look at more practical approaches to developing triggers. [edit] Complex Triggers As with functions and procedures it s possible to create simple triggers that are just 2 lines long, this includes a specification line and a single line of code. Unlike functions and procedures however it s unlikely that triggers will be writt en this way. It is also possible, again like functions and procedures, to create more complex triggers which take multiple lines of code to implement. To do this the BEGIN a nd END keywords are use to tell the compiler where the trigger code starts and e

nds. To write longer sections of code a semi colon ; is used to terminate the li nes therefore an alternative delimiter needs to be set. A common delimiter to us e in this instance is a double slash like so //, this is set in MySQL using the following syntax. DELIMITER // Once the delimiter has been set the standard MySQL procedural code can be used t o build the trigger body. You have a company that wants to pay its employees based on the number of charac ters in their name, a rather odd requirement but one which will suit our own req uirements to write a complex trigger. The first thing to do is decide which sort of trigger we will be writing. We want to apply the trigger on INSERT for every row effected. Because we want to work with data in the table and update values from within the trigger this needs to be a BEFORE trigger. Using the previously discussed naming convention this would give us the following trigger specificati on. CREATE TRIGGER bi_emps_fer BEFORE INSERT ON emps FOR EACH ROW This trigger will contain more than one line of code so we therefore need to con tain the code within a begin and end to let the compiler know where the statemen ts begin and end. There are a number of ways we could work out the number of cha racters in the employee s name but in this instance the standard MySQL function LE NGTH will be used. create trigger bi_emps_fer before insert on emps for each row begin declare namelength numeric; set namelength = length(new.emp_name); set new.salary = new.salary * namelength; end Download Trigger2.myp You may receive the following error message when you run the code. ERROR 1359 (HY000): Trigger already exists If you loaded the previous trigger creation script or have been writing your own triggers this message informs you that a trigger already exists of this name or the table you are creating already contains a procedure of the same type. [edit] DROP TRIGGER In the first instance it s possible to drop an existing trigger using the followin g syntax. In Version 5.0.10 and above use the schema name and then trigger name DROP TRIGGER schemaname.triggername; For versions older then 5.0.10 qualify the trigger name using the table name DROP TRIGGER tablename.triggername; Where tablename is the name of the table the trigger is associated with and trig

gername the name of the trigger you wish to drop. However if there is no trigger of the same name and you do not know the name of the trigger which is of the sa me type you can use the various trigger meta data available. We will look at tri gger metadata in a later section but if you need to find the name of the trigger s stored against a particular table use the following query. select trigger_schema, trigger_name, event_object_table from information_schema.triggers// ---------------- -------------- -------------------trigger_schema trigger_name event_object_table ---------------- -------------- -------------------pers bu_emp_fer emps ---------------- -------------- -------------------1 row in set (0.03 sec) So to address the possibility that the trigger exists already then a drop statem ent can be added to the script. drop trigger emps.bi_emps_fer // create trigger bi_emps_fer before insert on emps for each row begin declare namelength numeric; set namelength = length(new.emp_name); set new.salary = new.salary * namelength; end // Download Trigger3.myp After a slight diversion on to the subject of dropping triggers we can look at w hat the trigger is doing. It s relatively simple but does demonstrate that we can use ANSI standard procedural statements in the trigger body. We declare a variab le called namelength with a type of numeric, we then use namelength to hold the number of characters in the employees name fields. This is then used in a multip lication with the salary column. It s also worth noting that we can use the if exi sts keywords as we can when dropping stored procedures and functions, this isn t a major problem but means if the trigger doesn t exist then an error will be displa yed. To see if the the trigger fires correctly we need to insert a record into the em ps table. insert into emps values(4, JoJo , 1 ,null,1000) // Query OK, 1 row affected (0.00 sec) mysql> select * from emps where emp_id = 4 // -------- ---------- --------- ----------- --------emp_id emp_name dept_id dept_name salary -------- ---------- --------- ----------- --------4 JoJo 1 NULL 4000.00 -------- ---------- --------- ----------- --------1 row in set (0.00 sec) We can see that the original insert statement contains 1000 in the salary column , yet in the table we can see this value is 4000. This means the trigger fired s uccessful. To demonstrate that we can use other procedural code elements we can create the trigger using a while loop.

drop trigger emps.bi_emps_fer // create trigger bi_emps_fer before insert on emps for each row begin declare newsal numeric default 0; declare namelength, l_loop int default 0; set namelength = length(new.emp_name); while l_loop < namelength do set newsal := newsal set l_loop := l_loop end while; set new.salary = newsal; end insert into emps values(5, Andrew , 1 ,null,1000) // Query OK, 1 row affected (0.00 sec) select * from emps where emp_id = 5 // -------- ---------- --------- ----------emp_id emp_name dept_id dept_name -------- ---------- --------- ----------5 Andrew 1 NULL -------- ---------- --------- ----------1 row in set (0.01 sec) Download Trigger4.myp --------salary --------6000.00 --------new.salary; 1;

The trigger performs the same actions of the previous one but does so using a di fferent programming method. [edit] Referencing Tables in Triggers From version 5.0.10 it s now possible to issue SQL commands from within triggers. This opens up many more opportunities to use triggers in a useful manner. Being able to select from other tables allows you to populate columns automatically ba sed on other information in the table, take for example our emps table. create table emps(emp_id int NOT NULL, emp_name varchar(30), dept_id int, dept_name varchar(30), primary key(emp_id)) This table contains both a dept_id column and a dept_name column. We can use the dept_id to look up the associated value in the depts table but sometimes people store the description against the detail record also. This would be a time when it would be useful to be able to use selects in a trigger, on update or insert we could populate the description column automatically.

create trigger bi_emp_fer before insert on emps for each row begin declare l_dept_name varchar(30); select description into l_dept_name from dept where dept_id = new.dept_id; set new.dept_name = l_dept_name; end;// Query OK, 0 rows affected (0.01 sec) We can now insert just the dept_id and the dept_name will be updated using the c urrent value from the dept table. mysql> insert into emps (emp_id, emp_name,dept_id) values(4, Garry ,1); -> // Query OK, 1 row affected (0.00 sec) mysql> select * from emps; -> // -------- ---------- --------emp_id emp_name dept_id -------- ---------- --------1 Roger 1 2 John 2 3 Alan 1 4 Garry 1 -------- ---------- --------4 rows in set (0.00 sec)

-----------------------dept_name -----------------------NULL NULL NULL Information Technology ------------------------

--------salary --------2000.00 2500.00 2100.00 NULL ---------

This shows how useful it can be to select data from other tables from within a t rigger, it should be mentioned of course that this approach goes against the acc epted rules of normalization, but on many of the products I have worked on durin g my career there have been at least one table where this approach was used for reporting purposes. A common and very useful use of triggers is the implementation of auditing, we c an easily track changes to tables and columns using triggers. Lets say we had an audit table which stored the users user_name, the table they issued an insert a gainst and the date the insert took place. create table audit (user_name varchar(30), table_name varchar(30), update_date d ate)// Query OK, 0 rows affected (0.00 sec) We can then add a trigger one to any table we wish to keep track of changes to. create trigger bi_emps_fer before insert on emps for each row begin insert into audit (user_name, table_name, update_date) values (current_user() , emps ,now()); end; // Query OK, 0 rows affected (0.01 sec)

Now any changes to the emps table will be recorded in the audit table for us to check. insert into emps (emp_id, emp_name) values (6, Mike )// Query OK, 1 row affected (0.01 sec) select * from audit// ---------------- -----------user_name table_name ---------------- -----------root@localhost emps ---------------- -----------1 row in set (0.00 sec) [edit] Trigger Limitations It may seem strange to point out what MySQL triggers cannot do rather than spend ing the time discussing what they can do but by MySQL s own admission support for triggers is rudimentary (their term not mine). This has been addressed somewhat with the release of 5.0.10 and full support for SQL statements, but this section will look at how triggers work in other databases and highlight features which don t exist in MySQL. This hopefully will allow you to quickly identify if it s poss ible to convert your existing triggers to MySQL. [edit] Single trigger types per table The next limitation is the fact that you can only have one trigger type per tabl e. In some databases it s possible to enable and disable triggers, this means that they can in effect be turned on an off without the need to delete or reinstall the trigger. This is useful because we may have a trigger that is only used duri ng certain time periods or for debug purposes. Even though MySQL doesn t allow you to enable and disable triggers it would be of limited use because it would be i mpossible to split the triggers into individual items, the only option would be to have all the trigger code in the same trigger. Ignoring the enable and disable functionality the fact there you can only have o ne trigger type means that your triggers will be more complex and difficult to u nderstand as a number of different task will be performed in the same section of code. For example imagine if you had a number of date manipulation routines and a numb er of string manipulation routines for a table, it would be much more logical to split those 2 distinct sections of code into two triggers. Whether this is a big problem really depends on how complex your triggers will b e. [edit] No when conditions A common feature of triggers in other databases are when conditions. When condit ions are similar to a where clause on a select statement in that they restrict w hen the trigger fires based on a set criteria. Something we haven t mentioned so f ar is that there is an overhead when using triggers, it will reduce the speed of updates, inserts and deletes on a table. If there is no way to specify when a t rigger should fire it means that it will fire for every row on the table irrespe ctive of whether it will have an effect on the trigger code. Take the following trigger for example. create trigger bi_emps_fer before insert on emps for each row ------------update_date ------------2005-07-28 -------------

set new.emp_name := reverse(new.emp_name); This trigger deals only with the emp_name column but it will still fire if the e mp_name column has changed or not. This adds unnecessary overhead to transaction s within the database. This is particularly a problem with triggers that manipul ate columns that don t change often. Having said this it s also important to note th at the overhead of using triggers is preferable to using an external procedure. We can see the effect of not having the ability of restricting trigger to partic ular columns in the following example. create table triggtest (id numeric, name varchar(20)) // create begin set set set set set set set set set set set end // trigger bu_triggtest_fer before insert on triggtest for each row new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name = = = = = = = = = = = reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name); reverse(new.name);

create procedure triggproc() begin declare l_loop int default 0; loop1: loop set l_loop := l_loop 1; if l_loop >= 100000 then leave loop1; end if; insert into triggtest (id) values (l_loop); end loop loop1; end // call triggproc()// Query OK, 1 row affected (6.94 sec) drop trigger triggtest.bu_triggtest_fer // call triggproc()// Query OK, 1 row affected (5.42 sec) A simple table is created which contains just an id and name column. We then add a trigger on to the table, in this case it s rather contrived but it s possible tha t once the SQL restriction is lifted that a trigger will be performing a similar number of actions. A procedure is the created that inserts a number of rows int o the table. Calling this shows an execution time of 6.94 seconds. If the trigge r is then dropped the execution time is reduced to 5.42. This might seem only a small amount of time but with more complex functions in the trigger this time gr ows and grows.

create begin set set set set set set set set set set set end //

trigger bu_triggtest_fer before insert on triggtest for each row new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name = = = = = = = = = = = reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( reverse(upper(substring_index( A A A A A A A A A A A ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1))); ,1,1)));

call triggproc() // Query OK, 1 row affected (8.63 sec) Of course it should be noted that this overhead is offering us nothing as we are not even using the name column in the insert statement. [edit] Emulating When Conditions At the end of the last section we looked at the lack of support for when conditi ons (or something similar) in MySQL triggers and also looked at the negative imp act this could have on performance. This impact is a bigger problem in MySQL tha n some other procedural database languages because of the fact that only one tri gger can be created per table for each trigger type. This means that maintenance of triggers becomes more difficult with MySQL than other databases. For example in Oracle its possible to disable individual triggers temporarily say in the case of loading large amounts of data. As you can create individual trig gers for each job you can then enable and disable those triggers to remove the b urden on the various updates and inserts. What we need in MySQL is a way to emulate, at least in some part, the ability to only run triggers when a certain column is updated and not when each update or insert that takes place on the table. Traditionally the when condition is part o f the trigger specific and therefore not evaluated in the same way as say a trad itional IF statement. But its possible for us to code those when conditions insid e of the trigger body in MySQL in the form of IF statements. Take for example the following trigger code. drop trigger emps.bi_emps_fer // create trigger bi_emps_fer before insert on emps for each row begin declare newsal numeric default 0; declare namelength, l_loop int default 0; set namelength = length(new.emp_name); while l_loop < namelength do set newsal := newsal set l_loop := l_loop new.salary; 1;

end while; set new.salary = newsal; end Download trigger4.myp We may have a situation where a new name is not entered. In that case it would b e pointless performing any of the lines of the code, so we can add an IF stateme nt to simulate a when condition like so. drop trigger emps.bi_emps_fer // create trigger bi_emps_fer before insert on emps for each row begin declare newsal numeric default 0; declare namelength, l_loop int default 0; if new.emp_name is not null then set namelength = length(new.emp_name); while l_loop < namelength do set newsal := newsal set l_loop := l_loop end while; set new.salary = newsal; end if; end Download Trigger5.myp This time all we have done is add the IF and END IF around the code to stop it e xecuting when the emp_name column has no value. Its worth pointing out that we ha ve not enclosed the declare statements within the loop as these need to be place d before any other procedural statements. Because in this example we would be inserting a single row only the speed differ ence would be negligible, so if we look at a similar method to those used in the limitations section we can see the effect this has. create begin set set set set set set set set set set set end trigger bu_triggtest_fer before insert on triggtest for each row new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name new.name = = = = = = = = = = = reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); reverse(upper(substring_index(A,1,1))); new.salary; 1;

// call triggproc() // Query OK, 1 row affected (8.63 sec) drop trigger triggtest.bu_triggtest_fer // create trigger bu_triggtest_fer before insert on triggtest for each row begin if new.name is not null then set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); set new.name = reverse(upper(substring_index(A,1,1))); end if; end // call triggproc() // Query OK, 1 row affected (5.09 sec) This short test demonstrates that there is significant overhead when calling the trigger body even when the value in the column is blank. If the column is not b lank then the code will be executed. We can extend this further by checking the old and new values like so. drop trigger triggtest.bu_triggtest_fer // create trigger bu_triggtest_fer before insert on triggtest for each row begin if new.name <> new.old then set new.name = reverse(upper(substring_inde x(A,1,1))); . . . set new.name = reverse(upper(substring_index(A,1,1))); end if; end // call triggproc() // This is of use when creating update triggers rather than insert triggers. Using simple if statements we can at least emulate some of the benefit of when c onditions even if we can enable and disable individual components. [edit] Emulating Check Constraints Let me firstly give a brief idea of what check constraints are, in some other da tabases it s possible to use check constraints on a table to maintain data integri ty and enforce rules on the type and range of data that is entered into them. Fo r example you may have used other types of constraints with MySQL such as the Pr imary Key or Unique constraints which raise an error if a duplicate value is ent ered into a column. Check constraints are similar in that they are defined when creating the table, they differ however in the fact that the table creator can d

efine how the constraint operates rather than being limited to the rules defined in the small set that already exists. Lets say for example a client has a requirement that all salaries should be grea ter than 10 and less than 100. Using a check constraint we can simply check this when the insert takes place. Oracle allows the use of check constraints and the table creation script for this would be like so. create table emps ( emp_id number, salary number check (salary between 10 and 100) ); If you tried to insert a value outside of this range the Oracle would raise an e xception and stop the transaction taking place. As mentioned MySQL doesn t support check constraints as standard, so if we want to use them we need to come up with a method of checking the values being inserted into the tables, we could do this after using an update statement but at that p oint we won t know if the whole record should have been rejected or just the colum n which fails the check constraint check. What we need is a method of rejecting the record, or at least telling the user that a column is failing the constraint check at the point the value is inserted or updated in the table. The best way to do this is to use a trigger, however MySQL procedural language d oesn t currently support the independent raising of an error, prior to 5.0.10 it w as also not possible to call SQL to artificially raise an error, but even that i s not an acceptable solution because the error wouldn t have been specific enough to tell the user what went wrong. However with the release of 5.0.15 MySQL now offers us a solution, previously wh en an error was raised only the error number and standard message were returned to the user, from version 5.0.15 MySQL now also returns the value which you trie d to insert. So we can use this to our advantage to return a more meaningful err or message from a trigger. To do this we need to create two things, a table to p roduce the error against and a procedure to insert into that table which will in turn raise an error. It would be a good idea to create these in a separate data base so that they can be accessed by any database. The table definition is as fo llows. CREATE TABLE `Error` ( `ErrorGID` int(10) unsigned NOT NULL auto_increment, `Message` varchar(128) default NULL, `Created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, PRIMARY KEY (`ErrorGID`), UNIQUE KEY `MessageIndex` (`Message`)) ENGINE=MEMORY DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED COMMENT='The Fail() procedure writes to this table twice to force a constraint failure.' The important things to note about this table are the unique key in the Message column, this allows us to easily create the error and the fact the table uses th e MEMORY engine. This means the table is stored in memory and not in the file sy stem, using that we don t need to clear the table down or worry about the table be coming full. Next we need to create a procedure to insert into this table to raise the unique key violation, this stage isn t strictly necessary but makes the trigger code we

will produce in a moment much more readable. It also means we can consistently c all the same routine in any number of triggers (or even in Functions and Procedu res). DELIMITER $$ DROP PROCEDURE IF EXISTS `Fail`$$ CREATE PROCEDURE `Fail`(_Message VARCHAR(128)) BEGIN INSERT INTO Error (Message) VALUES (_Message); INSERT INTO Error (Message) VALUES (_Message); END$$ DELIMITER ; As you can see the procedure accepts a parameter called _Message, this is then u sed as the Message column value in the inserts. By calling the same insert state ment twice the unique_key constraint is violated on the error table, because MyS QL reports the column value in the error message this _Message parameter is adde d to the error message. We can call this procedure from the command line to see this in action. mysql> call fail( Salary must be over 10 ); ERROR 1062 (23000): Duplicate entry Salary must be over 10 for key 2 There are two things to note here, firstly we get our error message returned whi ch is great as it means the user can clearly see why the error was raised. The s econd thing you may notice is that the error number and description point to a d uplicate entry error, this isn t ideal but for now is the only way we can raise th e error. We now have the table and a procedure capable of raising an error safely without effecting any other part of the database. That s one of the key things here, we c ould raise the error without using a specific table or the procedure but doing s o won t effect any other part of the database. We now need to add this to a trigger so that the error is raised when the constr aint fails. We will use the salary example we mentioned earlier. I you have been using the pers schema you will have a table called emps, this table has a colum n called salary. Let s create a trigger which will enforce a constraint on that co lumn so that the value cannot be less than 10 and not higher than 100. DELIMITER $$ create trigger salary_check before insert on pers.emps for each row begin if new.salary < 10 or new.salary > 100 then call fail( Salary not in allowed range ); end if; end $$ DELIMITER ; The trigger simply checks the value of the new.salary column, if it s not within t he parameters allowed a call to the fail routine is called. This will then stop the record being inserted into the table. Let s look at that in action, first we c an take a look to see what values are in our emps table. mysql> select * from -------- ---------emp_id emp_name -------- ---------emps; --------- -------- -------dept_id salary bonus --------- -------- --------

0 Barry NULL 1 Paul 1 2 John 2 3 Alan 1 -------- ---------- --------4 rows in set (0.00 sec) We can first test that records

0.00 100.00 200.00 300.00 --------

NULL 100.00 100.00 100.00 --------

that have a valid salary are inserted correctly.

mysql> insert into emps (emp_id,emp_name,dept_id,salary) -> values (4, Sally ,1,90.00); Query OK, 1 row affected (0.06 sec) mysql> select * from emps; -------- ---------- --------- -------- -------emp_id emp_name dept_id salary bonus -------- ---------- --------- -------- -------0 Barry NULL 0.00 NULL 1 Paul 1 100.00 100.00 2 John 2 200.00 100.00 3 Alan 1 300.00 100.00 4 Sally 1 90.00 NULL -------- ---------- --------- -------- -------5 rows in set (0.00 sec) The record was inserted into the table without a problem, now we can try and ins ert a record which breaks the constraint and see what happens. mysql> insert into emps (emp_id,emp_name,dept_id,salary) -> values (5, Penny ,1,200.00); ERROR 1062 (23000): Duplicate entry Salary not in allowed range for key 2 This time the value wasn t in the range we have specified and the trigger raised t he error just as we wanted. This demonstrates how we can replicate check constraints in MySQL, there are how ever a couple of minor issues, firstly the error message while passing back a mo re readable format still isn t the correct error message, this could have conseque nces when updating or inserting records in a procedure which uses error handling , in particular handling the 1062 Duplicate Entry error specifically. The second problem is that MySQL currently only allows one trigger type per table, so you need to add the code to existing triggers, you also need to deal with the check for both Inserts and Updates individually because the two trigger types are inde pendent under MySQL. Having pointed out the limitations it s only fair then to say that this is an extr emely powerful method of implementing check constraints. Normally check constrai nts under other databases are fairly limited, checking a value in a range is abo ut as complicated as they get, but using this trigger method we can make them ve ry complex, the sky really is the limit. [edit] Trigger Metadata Triggers were a relatively late comer to the MySQL feature set, only being avail able from version 5.0.3 and upwards. However even at that stage they were primit ive and for a short period there was no metadata available for triggers. The onl y way to get any information about which triggers were available was to inspect the file system and view the .TRN and .TRG files. This limitation was removed in MySQL 5.0.10 with the introduction of a new infor mation schema table TRIGGERS. As with the other information schema tables TRIGGE RS allows you to inspect data on the triggers within the database using standard

SQL statements. To view the information that s available to us we can issue a des cribe on the table like so. mysql> DESC information_schema.triggers; ---------------------------- -------------Field Type ---------------------------- -------------TRIGGER_CATALOG varchar(512) TRIGGER_SCHEMA varchar(64) TRIGGER_NAME varchar(64) EVENT_MANIPULATION varchar(6) EVENT_OBJECT_CATALOG varchar(512) EVENT_OBJECT_SCHEMA varchar(64) EVENT_OBJECT_TABLE varchar(64) ACTION_ORDER bigint(4) ACTION_CONDITION longtext ACTION_STATEMENT longtext ACTION_ORIENTATION varchar(9) ACTION_TIMING varchar(6) ACTION_REFERENCE_OLD_TABLE varchar(64) ACTION_REFERENCE_NEW_TABLE varchar(64) ACTION_REFERENCE_OLD_ROW varchar(3) ACTION_REFERENCE_NEW_ROW varchar(3) CREATED datetime SQL_MODE longtext ---------------------------- -------------18 rows in set (0.11 sec)

-----Null -----YES NO NO NO YES NO NO NO YES NO NO NO YES YES NO NO YES NO ------

... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...

Note: I ve removed some of the columns returned from the describe command for spac e reasons. The TRIGGER_SCHEMA holds the database to which the trigger belongs, TRIGGER_NAME is the name of the trigger. The type of trigger, insert, update or delete, is h eld in the EVENT_MANIPULATION column.

Now you want to keep the changes of employee's data in another table whenever da ta of an employee's record changed. In order to do so you create a new table cal led employees_audit to keep track the changes. CREATE TABLE employees_audit ( id int(11) NOT NULL AUTO_INCREMENT, employeeNumber int(11) NOT NULL, lastname varchar(50) NOT NULL, changedon datetime DEFAULT NULL, action varchar(50) DEFAULT NULL, PRIMARY KEY (id) ) In order to keep track the changes of last name of employee we can create a trig ger that is fired before we make any update on the employees table. Here is the source code of the trigger DELIMITER $$ CREATE TRIGGER before_employee_update BEFORE UPDATE ON employees

FOR EACH ROW BEGIN INSERT INTO employees_audit SET action = 'update', employeeNumber = OLD.employeeNumber, lastname = OLD.lastname, changedon = NOW(); END$$ DELIMITER ; You can test the trigger which created by updating last name of any employee in employees table. Suppose we update last name of employee which has employee numb er is 3: UPDATE employees SET lastName = 'Phan' WHERE employeeNumber = 1056 Now when you can see the changes audited automatically in the employees_audit ta ble by executing the following query SELECT * FROM employees_audit In order to create a trigger you use the following syntax: CREATE TRIGGER trigger_name trigger_time trigger_event ON table_name FOR EACH ROW BEGIN ... END CREATE TRIGGER statement is used to create triggers. The trigger name should follow the naming convention [trigger time]_[table name] _[trigger event], for example before_employees_update Trigger activation time can be BEFORE or AFTER. You must specify the activation time when you define a trigger. You use BEFORE when you want to process action prior to the change being made in the table and AFTER if you need to process act ion after changes are made. Trigger event can be INSERT, UPDATE and DELETE. These events cause trigger to fi re and process logic inside trigger body. A trigger only can fire with one event . To define trigger which are fired by multiple events, you have to define multi ple triggers, one for each event. Be noted that any SQL statements make update d ata in database table will cause trigger to fire. For example, LOAD DATA stateme nt insert records into a table will also cause the trigger associated with that table to fire. A trigger must be associated with a specific table. Without a table trigger does not exist so you have to specify the table name after the ON keyword. You can write the logic between BEGIN and END block of the trigger. MySQL gives you OLD and NEW keyword to help you write trigger more efficient. Th e OLD keyword refers to the existing row before you update data and the NEW keyw ord refers to the new row after you update data. In this tutorial, you have learned how to create the first trigger in MySQL. You

've written the first trigger to audit changes of last name of employee in emplo yees table.

Vous aimerez peut-être aussi