Put simply, a Stored Procedure ("SP") is a procedure (written in SQL and other control statements) stored in a database which can be called by the database engine and connected programming languages.
In this tutorial, we will see how to create an SP in MySQL and execute it in MySQL server and in PHP.
Note: We are not going to cover the full aspect of the SP here. The official MySQL document should always be the place for reference.
SP are also available in other common database servers (Postgre for example) so what we will discuss here can be applied to those as well.
Why Stored Procedures are recommended
Most of us are quite familiar with the normal setup to build a database application: creating database, creating tables, set up indexes, CRUD the data, issue queries from the client side and do further processing if necessary.
That workflow works fine in most cases but there is one important aspect of database programming missing: the Stored Procedure.
There are at least four advantages I can think of to use an SP in a database application.
Firstly, it reduces the network traffic and overhead. In a typical PHP database web application, there are four layers:
- The client layer, which is normally a web browser. It receives user interactions and presents the data in a UI.
- The web server layer, which handles and dispatches user requests and sends back responses to the client layer.
- The PHP layer, which handles all PHP interpretation, does the application logic and generates the PHP part of response.
- The database layer, which handles all database queries, including but not limited to a
In a typical environment, these layers will most likely not reside on one single machine, maybe not even in one network, for larger applications.
Although network speed has tremendously increased in the past few years, it is still the slowest and most unreliable compared to other ways of transferring the data (CPU cache, memory, hard disk, etc). So, to save bandwidth and increase robustness, it is sometimes a good idea to have more processing and logic done on the server side (in particular, the MySQL server) and have less data transferred through the network.
Secondly, it improves the performance. SP is stored and run directly in the MySQL server. It can be pre-compiled and analyzed by the database server. This is quite different from issuing the same query from the client side, where the query will be parsed by database drivers, analyzed and optimized (if possible) every time the query statement is called. This is somehow quite like the interpreted language execution (at the client end) and the compiled language execution (at the database server end). And we know a compiled program will run faster.
Third, Write Once and Execute Anywhere. SQL is standard and purely 100% platform independent. It only relies on the database server. Consider how many different languages/libs there are that we can use to deal with the database. It increases efficiency to put the data retrieving and processing at the server end instead of writing the same processing logic in a different syntax provided by all these languages/libs, if the data processing logic is so commonly used.
Last but not least, SP is a fundamental aspect of database security.
Let's consider a simple database setup. In a human resource information system (HRIS), it is reasonable to assume that there exists a table holding the salary information of each employee. An HR employee should have the right to grab some figures out of this table: total salary, average salary, etc but this employee should not see the detailed salary of each employee as this information will be too sensitive and should only be available to a few.
We know MySQL has a comprehensive privilege control. In this case, it is obvious that we can't even grant
SELECT privilege to this HR employee (which, if we do, means he/she can see the detailed salary of everyone). But if he/she can't access the
salary table, how can this employee get the aggregation information related to
salary? How can we allow the employee to grab that information without compromising the HR policy?
The answer is using a Stored Procedure that returns the required information and grants that employee the
EXECUTE privilege. (For a detailed list and explanation of MySQL privileges, please consult the official documentation. The link here is for MySQL 5.6. Please replace 5.6 with the version you are using.)
SP is now a bridge, bridging the user (our HR employee) and the table (
salary), to which the user has no direct access.
That's it! With SP, we can get the user to accomplish the task without compromising the database security (and HR policy)!
Drawbacks of using Stored Procedures
After naming all the advantages of using an SP, we need to be clear about some of the drawbacks and see if there are ways to improve.
- No version control on SP itself. When an SP is modified, it is modified, no historical trails can be kept in the server side. It may create some frustrations when the user would like to rollback a change. My suggestion is to write the SP in your client side and put it under version control. When the SP is ready, it is easy to copy the code into, say MySQL Workbench and create it at the server side. By doing so, we can have some degree of version control.
- No easy way to "synchronize" the changes applied and force everyone to use the latest version, in particular, when each of the team member has his/her own local database for development and testing purposes. Version control may be the solution but still requires manual intervention by updating the local copy of the SP in the local db server. Another way is to use "mocking". The team members can be divided so that at least one person will focus on the maintenance of the SP and the implementation of the calling to SP in the code. All others that need the results from the SP can develop and test their portion using mocking objects, i.e, always assuming the "faked" call to the SP will return a desired result. In a later stage, merging can be done to discard the mocking code.
- Hard to backup/export. The SP is on the server side. Regular developers will only have basic privileges (
EXECUTE, etc) and no admin rights to backup and export. In a way, I won't call it a drawback but rather a fundamental aspect of db security. There is no way, and it is not recommended to get around this. It is suggested that, in a team, a dedicated DB admin will be appointed to do such jobs. A regular db backup can also serve the backup/export (and import) purpose.
Creating a Stored Procedure in MySQL
As SPs are stored in the server, it is recommended to create the SP directly in the server, i.e., not by using PHP or other programming languages to issue SQL commands to do so.
Let's see how to create the SP in MySQL server, create a user and apply privileges and run (as that user) the SP to see if the result is correct. In my working environment, I am using MySQL Workbench. Other tools are available (PHPMyAdmin for example) so feel free to use the tools that best fit you.
Assume we have a table like this:
CREATE TABLE `salary` ( `empid` int(11) NOT NULL, `sal` int(11) DEFAULT NULL, PRIMARY KEY (`empid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And for the HR employee that needs to get the aggregated information on salary (average, max, min, etc) from that table, we first create a user
'tr' like this:
CREATE USER 'tr'@'localhost' IDENTIFIED BY 'mypass';
and for this user, we only grant
EXECUTE privilege to the schema where the
salary table resides:
grant execute on hris.* to tr@`%`
We can verify the necessary privilege is granted by visiting the "Users and Privileges" in MySQL Bench:
Now let's create the SP like this:
DELIMITER $$ CREATE PROCEDURE `avg_sal`(out avg_sal decimal) BEGIN select avg(sal) into avg_sal from salary; END
NOTE: All the above operations will require an admin role in the MySQL server.
After issuing the command in MySQL Workbench, the
avg_sal SP will be created and ready to be called. It will return the average salary of table
To test if the user
tr can actually run the SP but should not be able to access the
salary table, we can switch the role by logging into the MySQL server using user
tr. It can be done by creating another connection in MySQL Workbench using a different user/password pair.
After logging in as
tr, the first thing we will notice is that the user will not be able to see any tables and can only see the SP:
It is clear that the user
tr won't be able to select anything from any table (thus unable to see the detailed salary number of the
salary table) but he/she is able to execute the SP we just created and get the average salary of the company:
call avg_sal(@out); select @out;
The average salary will be displayed.
We have so far done all the preparation to create a user, grant privileges, create a SP and test the running of the SP. Next we will show how to call that SP from within PHP.
Calling a Stored Procedure from PHP
With PDO, calling an SP is straightforward. The PHP code is as follows:
$dbms = 'mysql'; //Replace the below connection parameters to fit your environment $host = '192.168.1.8'; $db = 'hris'; $user = 'tr'; $pass = 'mypass'; $dsn = "$dbms:host=$host;dbname=$db"; $cn=new PDO($dsn, $user, $pass); $q=$cn->exec('call avg_sal(@out)'); $res=$cn->query('select @out')->fetchAll(); print_r($res);
$res will contain the average salary of the table
salary. The user can further process the output with PHP now.
In this article, we reviewed the long-forgotten component in MySQL database: the Stored Procedure. The advantages to use an SP are obvious and let me re-emphasize: Stored Procedures allow us to apply stronger database access control to certain data to fit in with the business requirement.
We also illustrated the basic steps to create Stored Procedures, create a user and assign a privilege, and how to call it in PHP.
This article does not cover the full scope of Stored Procedures. Some important aspects like input/output parameters, control statement, cursors, the complete syntax, etc are not discussed in this short article.
If you feel interested, please leave your comments here and we will be glad to bring more in-depth articles about this useful and powerful aspect of MySQL.