Stored Procedure
Stored Procedure
Stored Procedure
Stored routines (procedures and functions) are supported in MySQL 5.0. A stored
procedure is a set of SQL statements that can be stored in the server. Once this has been
done, clients don't need to keep reissuing the individual statements but can refer to the
stored procedure instead.
Answers to some questions that are commonly asked regarding stored routines in MySQL
can be found in Section A.4, “MySQL 5.0 FAQ — Stored Procedures”.
MySQL Enterprise. For expert advice on using stored procedures and functions
subscribe to the MySQL Network Monitoring and Advisory Service. For more
information see http://www.mysql.com/products/enterprise/advisors.html.
Stored routines can provide improved performance because less information needs to be
sent between the server and the client. The tradeoff is that this does increase the load on
the database server because more of the work is done on the server side and less is done
on the client (application) side. Consider this if many client machines (such as Web
servers) are serviced by only one or a few database servers.
Stored routines also allow you to have libraries of functions in the database server. This is
a feature shared by modern application languages that allow such design internally (for
example, by using classes). Using these client application language features is beneficial
for the programmer even outside the scope of database use.
MySQL follows the SQL:2003 syntax for stored routines, which is also used by IBM's
DB2.
The MySQL implementation of stored routines is still in progress. All syntax described in
this chapter is supported and any limitations and extensions are documented where
appropriate. Further discussion of restrictions on use of stored routines is given in
Section F.1, “Restrictions on Stored Routines and Triggers”.
Binary logging for stored routines takes place as described in Section 18.4, “Binary
Logging of Stored Routines and Triggers”.
Recursive stored procedures are disabled by default, but can be enabled on the server by
setting the max_sp_recursion_depth server system variable to a nonzero value. See
Section 5.2.3, “System Variables”, for more information.
Stored functions cannot be recursive. See Section F.1, “Restrictions on Stored Routines
and Triggers”.
User Comments
Posted by [name withheld] on February 7 2004 5:00pm [Delete] [Edit]
You CAN return a result set from a procedure. I was under the impression that it was not
possible because the documentation is a bit, well... lacking in useful examples. It does not
say anything about returning a table as your result of the procedure, at least I didnt see
that, maybe I missed it. Frankly, if it didnt return a result set, I would have no use for
stored procedures.
You can see in the 'call sp...' line, that it is not followed by a 'select @parameter' satement
(as it does in the documentation), you just call it and it returns the records.
Also, you do not use parameters within your procedure using an '@'. In the example
above, you do not do: 'If @OB = 'LastName' then'. Leave the '@' off of it. This one really
messed me up.
I hope this helps someone out there, I just spent hours trying to figure this out.
Good luck!
I believe that the current MySQL online poll asking for the features that developers such
as us are most looking forward to is very enlightening to this topic. At present, the
number 1 feature is Stored Procedures. I have seen the range of comments both here and
in other forums equating SP's to both bad and good practice. I began my SQL by
avoiding SPs entirely as I had read they were somehow bad practice. As my SQL skills
improved, I began experimenting with Stored Procedures and found that, contrary to the
naysayers, SP's are exceptionally valuable. I have slowly and steadily converted the vast
majority of the Project Management database that I wrote and administer to using SP's
for most data transactions. This has paid off by reducing code and unifying the methods
used to update and insert data across the entire application. I found that I had coded
certain data transactions in different manners in different areas of the application even
though they should have been the same. SP's allowed me to unify this. The other huge
advantage is, of course, precompiling the SP (SQL Server 2000) gives huge speed
advantages. The T-SQL language gives me the ability to call an SP from the application,
and then have the SP execute many various queries and updates contained within
transactions that do lock checks and deadlock handling. I have many 10 page SP's that
run huge amounts of data transactions and exit in milliseconds. I also appreciate the fact
that SP's run on the server, not the client and reduce network traffic. This has given my
application huge speed increases and reduced our network traffic to almost nothing. I am
very much looking forward to complete implementation of Stored Procedures in MySQL
and think that I will likely port the entire application out of MS SQL Server 2000 as soon
as this is done. The only other features I need to be able to do this are Jobs that can be run
by the server on schedules and MySQL also needs to improve Transactions.
In regards to other comments about using SP's to use cursors to process data... I HIGHLY
discourage the use of cursors. To quote another SQL professional that I sadly foget his
name "Cursors are Evil". The fact is that you can probably rewrite nearly any task using a
cursor into a SQL statement (or set of SQL statements). Cursors run extremely slooooow
compared to standard ANSI SQL statements. When my SQL skills were quite novice, I
used a SP to run a cursor to convert huge blocks of data and import them into new tables.
The procedure took just over 2 hours to process 11GB of data. When my SQL skills
improved, I rewrote that SP, removing the cursor and replacing it with a complex, nested,
pure SQL statement. The new SP ran in 5 minutes and did the entire 11GB data
processing. Cursors should be avoided at all costs in a production environment. In lieu of
using a cursor, I recommend you learn more about SQL.
The prior comment about the usefulness of stored procedures is TRUE; the comment
about avoiding cursors at all costs is FALSE.
One must always use the appropriate tool in its most advantageous context. One bad
programming choice does not prove that the tool is bad.
For example, it is possible for a compiler to prepare the SQL in a declared CURSOR in
advance, so that repeated fetches of the cursor do not require repeated prepares of the
same SQL. This can avoid a huge amount of server traffic, especially for one-row result
sets of primary key look-ups.
We don't yet know how MySQL compiles cursors, but Oracle cursors can be many times
FASTER in the example i just gave.
There are too few examples about stored procedures up to now. I post a simple example
here and I hope it is useful to beginners of MySQL like me :)
) type=innodb;
create table catagory_set
(
master_id int unsigned not null,
slave_id int unsigned not null,
index(master_id),
index(slave_id),
primary key (master_id,slave_id),
foreign key (master_id) references catagory (catagory_id) on delete cascade,
foreign key (slave_id) references catagory (catagory_id) on delete cascade
) type=innodb;
# Insert a subcatagory #
if param1 > 0 then
# Check if the same catagory name exist and the master catagory #
select count(catagory_id) into name_exist from catagory, catagory_set
where catagory.name=param2 and catagory.catagory_id=catagory_set.slave_id
and catagory_set.master_id=master_id;
end if;
# Insert a primary catagory #
else
# Search and compare the name of all primary catagory #
select count(catagory_id) into name_exist from catagory
where name = param2 and not exists(
select * from catagory_set
where catagory_set.slave_id = catagory.catagory_id
);
if name_exist > 0 then
set error_msg = 'The catagory name already exist, please choose another name';
else
insert into catagory values (null, param2, param3);
select last_insert_id() into cid;
end if;
end if;
end ?
delimiter ;
call add_catagory(1,'Planet','Earth',@cid,@error);
select @cid, @error;
Here is some code I used to geocode a dataset based on the NG Field and parcel number
(UK Ordinance survey) Useful if you are working with GIS data.
I have cut out some of the case statement as it is very repetitive and you can easily look it
up, mainly this example show how you can manipulate data then use your result to update
existing tables.
delimiter //
Confused?!
Read this:
http://dev.mysql.com/tech-resources/articles/mysql-storedprocedures.pdf
Just a comment:
Using SQL stored procedures has been the only correct way to write web applications
when using oracle, mssql or other enterprise database servers(meaning ones people have
to pay for) - The introduction of SPs in MySQL was a long time coming and it now puts
it on the map as a serious alternative to the RDBMSs named above. I never considered it
a serious alternative for any project before it had SPs and I think there are many who feel
this way. Hats off to the team of people who made this happen, it will be big like never
before.
MySQL as of version 5.0.15 does not support recursive stored procedures. This is
contrary to the 'hierarchy2' example found here http://dev.mysql.com/tech-
resources/articles/mysql-storedprocedures.pdf
Maybe I'm the only one to have this problem, but I noticed that since the variable names
don't start with "@" or anything to signify them as variables, you have to be very careful
about naming. For example I tried to run:
DELIMITER |
CREATE PROCEDURE sp_getUserInfo(IN userID INT)
BEGIN
SELECT * FROM users WHERE userID = userID;
END|
It returned all rows in the table. I was very curious about why this was happening, then I
changed the variable name to _userID and it only returned the correct row. There aren't
many examples and this might cost people some time trying to figure out what's going
on.
Posted by Paul Pikowsky on February 24 2006 6:01pm [Delete] [Edit]
You can get a Selection set back from a procedure, but apparently you can't just replace a
Select statement in PHP with a Call statement.
@ Joel Hoard:
Thatswhy i always use my fields this way: `table`.`field` Then you could have
`table`.`userid` = userid :)
You can make a procedure act like a select and hence return a result set.
I was initially confused by statements that procedures could not return values. It appears
that as the person above mentioned temporary tables or as my example shows using
prepared statements can achieve a returned value.
Try it out calling it from command line mysql, you will see a lovely result set returned.
NOTE: since dynamic sql statements are not allowed in functions this only works in
stored procedures.
How to pass arrays to stored procedures from php to isolate records using the "WHERE
col IN (param_list)" syntax. Examples of how to delete multiple rows and select multiple
rows.
Example:
$id_array = array(3,6,7,12,78);
$id_list = implode(',', $id_array);
Since "WHERE id IN ()" only accepts specified params (a, b, n) and not a comma-
separated list ('a,b,n'), we need to either supply each parameter which is useless since we
usually don't know how many we expect, or we could delete multiple records one by one
in a loop:
But this seems to be a bad solution also since we call the database count($id_array) times.
Better to make the loop inside a procedure:
Now we can retrive the ids easily in both SELECT and DELETE procedures:
The DELETE procedure could even be trimmed, so string splitting and deletion happens
in the same procedure:
END
Hope this helps :) Suggestions, corrections and better solutions are welcome!
@ Joseph Wilk
Thanks for the example of using the Prepare and Execute statements. This sort of
statement allows for great flexiblity in writing dyanamic SQL. For example, I have
written stored procedures that generate a string that created standard CRUD (Create,
Retrieve, Update, Delete) procedures given only the table name as an input. It querried
the metadata to get column names and identify primary keys and so forth. For big
projects, this was a time saver and you cannot do this sort of 'meta programming' without
an execute command that can run arbitrary SQL. This is the only time that I can recall
neededing to use an execute statement in a stored procedure.
The fact that a prepare statement is in a stored procedure means that the SQL within the
stored procedure cannot be optimized when the stored procedure is created. This means
that either the SQL is not optimized or it must be optimized each time the procedure is
called. I don't know which of these MySQL does, but you are better off avoiding the
'execute' statement when performance is an issue. (Performance was not an issue in my
example, the sproc was run once per table during development & the sproc was much
faster than a developer)
The prepare and execute statements are powerful, but they should only be used if there is
no other reasonable option.
@Paul Pikowsky
Actually, it will work, you just need to make sure you can handle multiple result sets
being returned. I don't know how that works in PHP, but I have seen the "can't return a
result set in the given context" error in other languages (Java, C++, Python, Tcl) and it
was all because of the need to be able to handle multiple result sets (that is a mysql flag
which should be passable in your connection or otherwise able to be set). Good luck
finding how to set that flag in PHP.
HERE IT IS:
For all of you getting "can't return a result set in the given context" errors when using
PHP to execute stored procedures,
the mysql_connect flag is:
Try the following if, like me, you have had difficulties making dynamic references to
tables work. Let's say you have a series of tables, identical in structure `tbl_1`, `tbl_2`,
`tbl_3`, etc... that you wish to work on from one stored procedure.
Rather than using excessive SET, PREPARE, EXECUTE statements (with their many
restrictions), use ALTER TABLE (RENAME TABLE doesn't work for temporary tables)
to rename the table to a fixed name at the start of the stored procedure and revert to the
original name at the end. This allows the procedure to flow smoothly and efficiently and
avoids problems with variables. A major benefit is also that it allows you to return a
recordset from the SELECT statement, which is not possible if you construct the query
using SET PREPARE EXECUTE. Example:
/*---------------------------------------------------------------------------------------------------------
-*/
DELIMITER $$
DROP PROCEDURE IF EXISTS proc_name$$
CREATE PROCEDURE proc_name(IN table_num INT)
BEGIN
/* rename table */
SET @s = CONCAT("ALTER TABLE tbl_", table_num, " RENAME tbl_tempname;");
PREPARE stmt FROM @s;
EXECUTE stmt;
/* your code then goes in the middle here with any references to your table like this, etc.
*/
SELECT * FROM tbl_tempname;
END$$
DELIMITER ;
/*---------------------------------------------------------------------------------------------------------
-*/
When calling the routine, you'd pass the table number part in something like this, i.e. to
work on `tbl_2`
CALL proc_name(2);
For editing or updating a row the same procedure can be used. Here's an example of the
style I use. Passing zero as the ID inserts, otherwise it updates
DELIMITER $$
DROP PROCEDURE IF EXISTS `link_edit`$$
CREATE PROCEDURE `io`.`link_edit` (_link_id int, _title varchar(20), _url
varchar(150))
/* Inserts or update a web link, 0 as link_id means insert
returns the new or updated link row
*/
BEGIN
declare _new_id int;
case _link_id
/* new link */
when 0 then
insert into links (title, url) values(_title, _url);
select last_insert_id() into _link_id;
/* update link */
else
update links set title=_title, url=_url where link_id=_link_id;
end case;
/* return Row */
select link_id, title, url from links where link_id = _link_id;
END$$
DELIMITER ;
As a note to help others wanting to call a procedure from within a procedure: To call a
stored procedure within a stored procedure, and get the result out, you must use an
"OUT" variable, and call the procedure with it.
E.g.
CALL proc1(data,result);
/* result is now populated with the result from proc1 */
SELECT result;
I thought this was not obvious from the current documentation - hope it helps someone.
The comments here are the best documentation of stored procedures so far - thanks!
Add your own comment.