{"id":93857,"date":"2022-04-15T16:19:47","date_gmt":"2022-04-15T16:19:47","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=93857"},"modified":"2022-04-15T16:23:23","modified_gmt":"2022-04-15T16:23:23","slug":"working-with-mysql-stored-procedures","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/working-with-mysql-stored-procedures\/","title":{"rendered":"Working with MySQL Stored Procedures"},"content":{"rendered":"<p><strong>This article is part of Robert Sheldon's continuing series on Learning MySQL. To see all of the items in the series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/mysql-coding-basics\/\">click here<\/a>.<\/strong><\/p>\n\n<p>Like most relational database management systems, MySQL supports the use of stored procedures that can be invoked on-demand by data-driven applications. Each stored procedure is a named database object that contains a routine made up of one or more SQL statements. When an application calls the stored procedure, MySQL executes those statements and returns the results to the application.\u00a0<\/p>\n<p>A procedure\u2019s routine can include a wide range of statements, including data definition language (DDL) and data manipulation language (DML). MySQL stored procedures also support the use of input and output parameters, making them a highly flexible tool for encapsulating statement logic.<\/p>\n<p>Stored procedures enable SQL code to be reused as often as needed, helping to simplify application development and reduce statement errors. Developers don\u2019t have to write complex queries for each application request, and QA teams don\u2019t need to spend as much time verifying queries when testing applications.<\/p>\n<p>The ability to reuse code also reduces network traffic because a stored procedure can be invoked with a single <code>CALL<\/code> statement, no matter how complex the underlying query. Stored procedures can also deliver a higher degree of security by abstracting the underlying database structure and eliminating ad hoc queries at the application level.<\/p>\n<p>In this article, I demonstrate how to create and update stored procedures, as well as invoke them with a <code>CALL<\/code> statement. You\u2019ll learn how to build both basic and parameterized procedures that use input and output parameters. As with the previous articles in this series, I used the MySQL Community edition on a Windows computer to build the examples, which I created in MySQL Workbench, the graphical user interface (GUI) that comes with the Community edition.<\/p>\n<h2>Preparing your MySQL environment<\/h2>\n<p>The examples in this article are based on the <code>travel<\/code> database, which is the same database I used for the previous article on <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/working-with-mysql-views\/\">MySQL views<\/a>. This article uses the same tables and data to demonstrate how to work with stored procedures. If you tried the examples in the previous article, the <code>travel<\/code> database might still be installed on your MySQL instance. If it is not, you can use the following SQL script to create the database and its tables:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk \">DROP DATABASE IF EXISTS travel;\r\nCREATE DATABASE travel;\r\nUSE travel;\r\nCREATE TABLE manufacturers (\r\n  manufacturer_id INT UNSIGNED NOT NULL AUTO_INCREMENT,\r\n  manufacturer VARCHAR(50) NOT NULL,\r\n  create_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\r\n  last_update TIMESTAMP NOT NULL \r\n    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (manufacturer_id) ) \r\nENGINE=InnoDB AUTO_INCREMENT=1001;\r\nCREATE TABLE airplanes (\r\n  plane_id INT UNSIGNED NOT NULL AUTO_INCREMENT,\r\n  plane VARCHAR(50) NOT NULL,\r\n  manufacturer_id INT UNSIGNED NOT NULL,\r\n  engine_type VARCHAR(50) NOT NULL,\r\n  engine_count TINYINT NOT NULL,\r\n  max_weight MEDIUMINT UNSIGNED NOT NULL,\r\n  wingspan DECIMAL(5,2) NOT NULL,\r\n  plane_length DECIMAL(5,2) NOT NULL,\r\n  parking_area INT GENERATED ALWAYS AS \r\n              ((wingspan * plane_length)) STORED,\r\n  icao_code CHAR(4) NOT NULL,\r\n  create_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\r\n  last_update TIMESTAMP NOT NULL \r\n    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (plane_id),\r\n  CONSTRAINT fk_manufacturer_id FOREIGN KEY (manufacturer_id) \r\n    REFERENCES manufacturers (manufacturer_id) ) \r\nENGINE=InnoDB AUTO_INCREMENT=101;<\/pre>\n<p>The <code>airplanes<\/code> table includes a foreign key that references the <code>manufacturers<\/code> table, so be sure to create the tables in the order shown here. After you create the tables, you can add sample data to them so you\u2019ll be able to test your stored procedure. To populate the table, run the following <code>INSERT<\/code> statements:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">INSERT INTO manufacturers (manufacturer)\r\nVALUES ('Airbus'), ('Beechcraft'), ('Piper');\r\nINSERT INTO airplanes \r\n  (plane, manufacturer_id, engine_type, engine_count, \r\n    max_weight, wingspan, plane_length, icao_code)\r\nVALUES \r\n  ('A380-800', 1001, 'jet', 4, 1267658, 261.65, 238.62, 'A388'),\r\n  ('A319neo Sharklet', 1001, 'jet', 2, 166449, 117.45, 111.02, 'A319'),\r\n  ('ACJ320neo (Corporate Jet version)', 1001, 'jet', 2, 174165, 117.45, 123.27, 'A320'),\r\n  ('A300-200 (A300-C4-200, F4-200)', 1001, 'jet', 2, 363760, 147.08, 175.50, 'A30B'),\r\n  ('Beech 390 Premier I, IA, II (Raytheon Premier I)', 1002, 'jet', 2, 12500, 44.50, 46.00, 'PRM1'),\r\n  ('Beechjet 400 (from\/same as MU-300-10 Diamond II)', 1002, 'jet', 2, 15780, 43.50, 48.42, 'BE40'),\r\n  ('1900D', 1002, 'Turboprop', 2,17120,  57.75, 57.67, 'B190'),\r\n  ('PA-24-400 Comanche', 1003, 'piston', 1, 3600, 36.00, 24.79, 'PA24'),\r\n  ('PA-46-600TP Malibu Meridian, M600', 1003, 'Turboprop', 1, 6000, 43.17, 29.60, 'P46T'),\r\n  ('J-3 Cub', 1003, 'piston', 1, 1220, 38.00, 22.42, 'J3');<\/pre>\n<p>As with the <code>CREATE<\/code> <code>TABLE<\/code> statements, you should run the <code>INSERT<\/code> statements in the order specified here so you don\u2019t violate the foreign key defined on the <code>airplanes<\/code> table.<\/p>\n<h2>Creating a stored procedure in MySQL<\/h2>\n<p>To build a stored procedure in MySQL, you must use a <code>CREATE<\/code> <code>PROCEDURE<\/code> statement. To get started, open a new query window in Workbench and ensure that the target database is active. (To make a database active, double-click the database in Navigator or run a <code>USE<\/code> statement.) For this example, you\u2019ll use the <code>travel<\/code> database.<\/p>\n<p>When building your <code>CREATE<\/code> <code>PROCEDURE<\/code> statement, you must provide a name for the procedure and specify the SQL routine you want to persist to your database. The routine can include a single SQL statement such as <code>SELECT<\/code> or <code>UPDATE<\/code>, or it can be a compound statement. A compound statement is one that uses the <code>BEGIN\u2026END<\/code> syntax to enclose a block of one or more SQL statements. The block can include a wide range of SQL language elements, including DDL and DML statements, variable declarations, embedded blocks, or flow control constructs such as loops or conditional tests.<\/p>\n<p>Most stored procedures use a compound statement even if they include only a single SQL statement. For example, the routine in the following <code>CREATE<\/code> <code>PROCEDURE<\/code> statement includes a compound statement with only one <code>SELECT<\/code> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELIMITER \/\/\r\nCREATE PROCEDURE get_plane_info()\r\nBEGIN\r\n  SELECT a.manufacturer_id, m.manufacturer, \r\n    COUNT(*) AS plane_count,\r\n    ROUND(AVG(a.wingspan), 2) AS avg_span, \r\n    ROUND(AVG(a.plane_length), 2) AS avg_length\r\n  FROM airplanes a INNER JOIN manufacturers m\r\n    ON a.manufacturer_id = m.manufacturer_id\r\n  GROUP BY a.manufacturer_id\r\n  ORDER BY m.manufacturer;\r\nEND\/\/\r\n  \r\nDELIMITER ;<\/pre>\n<p>The example creates a procedure named <code>get_plane_info<\/code>. Notice that a set of parentheses follows the name. If the statement were to include input or output parameters, they would be defined within the parenthesis (which I\u2019ll cover later in the article). If you don\u2019t include parameters, you must still provide the parentheses.<\/p>\n<p>The compound statement is defined by the <code>BEGIN\u2026END<\/code> syntax, which encloses the single <code>SELECT<\/code> statement. The <code>SELECT<\/code> statement itself joins the <code>airplanes<\/code> and <code>manufacturers<\/code> tables, groups the data by the <code>manufacturer_id<\/code> column in the <code>airplanes<\/code> table, and calculates the average <code>wingspan<\/code> and <code>plane_length<\/code> values for each manufacturer. The statement also orders the results by manufacturer and provides the total number of plane models for each one. (We\u2019ll be covering all these statement elements in more detail later in this series.)<\/p>\n<p>As you can see, creating a simple stored procedure is a fairly straightforward process. At a minimum, you must provide a name and the routine body. However, you no doubt noticed the inclusion of the two <code>DELIMITER<\/code> statements that surround the procedure definition.<\/p>\n<p>By default, MySQL uses the semi-colon (;) as a statement delimiter. This helps to ensure that a client sends a statement to the server in its entirety without confusing it with other statements. However, a compound statement within a stored procedure might include one or more delimiters, in addition to the definition\u2019s final delimiter, and all these delimiters can cause confusion when passing the <code>CREATE<\/code> <code>PROCEDURE<\/code> statement from a client to the server.<\/p>\n<p>To get around this issue, MySQL supports the use of the <code>DELIMITER<\/code> statement, which lets you temporarily change the delimiter so you can pass the entire procedure definition to the server as a single statement. In the above example, the first <code>DELIMITER<\/code> statement changes the delimiter to double forward slashes (\/\/), and the second <code>DELIMITER<\/code> statement changes the delimiter back to a semi-colon. The temporary delimiter is then used at the end of the <code>CREATE<\/code> <code>PROCEDURE<\/code> statement (after the <code>END<\/code> keyword), but the <code>SELECT<\/code> statement itself is still terminated with the semi-colon delimiter.<\/p>\n<p>I also wanted to point out that MySQL Workbench provides a tool (in the form of a tab) for creating and editing stored procedures. The tool is similar to the one used for creating and editing views. It provides a stub for building a <code>CREATE<\/code> <code>PROCEDURE<\/code> statement but leaves it up to you to fill in the details. Figure 1 shows the <em>Stored Procedure<\/em> tab as it appears when you first launch it in Workbench.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93858\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-email-6.png\" alt=\"The Create Procedure dialog. It has the stub of the proc, CREATE PROCEDURE 'new_procedure'() BEGIN END\" width=\"783\" height=\"565\" \/><\/p>\n<p class=\"caption\">Figure 1. Adding a stored procedure through the Workbench GUI<\/p>\n<p>To launch the <em>Stored Procedure<\/em> tab, select the target database in Navigator and then click the create stored procedure button on the Workbench toolbar. (The button includes the tooltip <em>Create a new stored procedure in the active schema in the connected server<\/em>.) When the <em>Stored Procedure<\/em> tab appears, you can start building your statement. After you finish, click <em>Apply.<\/em> MySQL will then add a few statement components that are necessary to create the procedure. Review the final script, click <em>Apply<\/em> once more, and then click <em>Finish<\/em>. The stored procedure will be added to the target database.<\/p>\n<p>The <em>Stored Procedure<\/em> tab can be useful for creating and editing a stored procedure, so I wanted to be sure you knew it\u2019s available. However, I prefer to use a query tab when working with a stored procedure because I think it\u2019s easier and saves steps, so this is the approach I take for the examples in this article.<\/p>\n<h2>Verifying a newly created stored procedure<\/h2>\n<p>After you run the <code>CREATE<\/code> <code>PROCEDURE<\/code> statement, you can verify that it\u2019s been added to the <code>travel<\/code> database by viewing it in Navigator, as shown in Figure 2. (You might need to refresh Navigator to see the new procedure.)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93859\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-1.png\" alt=\"Image showing the Navigator. Under Travel, Stored Procedures, the new proc get_plane_info is selected\" width=\"343\" height=\"648\" \/><\/p>\n<p class=\"caption\">Figure 2. Viewing the stored procedure in Navigator<\/p>\n<p>From Navigator, you can open the procedure definition in the <em>Stored Procedure<\/em> tab by clicking the wrench icon next to the procedure name. Figure 3 shows the procedure definition as you created it, except for one difference. It now includes the <code>DEFINER<\/code> clause after the <code>CREATE<\/code> keyword.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93860\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-2.png\" alt=\"Image showing get_plane_info in the stored procedure dialog. The code is the same as before without the DELIMITER lines and has added DEFINER='root'@'locatlhost' between CREATE and PROCEDURE \" width=\"780\" height=\"563\" \/><\/p>\n<p class=\"caption\">Figure 3. Viewing the procedure definition on the <em>Stored Procedure<\/em> tab<\/p>\n<p>The <code>DEFINER<\/code> clause specifies which account has been designated as the procedure creator. Because I created the stored procedure under the <code>root<\/code> account on my local MySQL instance, that is the username added to the definition. By default, MySQL uses the account of the user who executed the <code>CREATE<\/code> <code>PROCEDURE<\/code> statement, but you can specify a different account as long as it\u2019s been granted adequate permissions.<\/p>\n<p>Other than the <code>DEFINER<\/code> clause, your stored procedure definition should look much like what you created, except that there are no <code>DELIMITER<\/code> statements or custom delimiter. However, if you were to update the definition on the <em>Stored Procedure<\/em> tab and click <em>Apply,<\/em> Workbench would add those elements for you.<\/p>\n<p>Another way to verify whether your stored procedure has been created is to query the <code>routines<\/code> view in the <code>INFORMATION_SCHEMA<\/code> database:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT * FROM information_schema.routines\r\nWHERE routine_schema = 'travel';<\/pre>\n<p>The statement includes a <code>WHERE<\/code> clause that limits the results to the <code>travel<\/code> database. Any routines (stored procedures or functions) that have been created in the database will be returned by this query.<\/p>\n<p>You can limit the results further by also specifying the procedure name in the <code>WHERE<\/code> clause and by specifying which column or columns to return. For example, the following <code>SELECT<\/code> statement limits the results to the <code>routine_definition<\/code> column and the <code>get_plane_info<\/code> routine in the <code>travel<\/code> database:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT routine_definition\r\nFROM information_schema.routines\r\nWHERE routine_schema = 'travel'\r\n  AND routine_name = 'get_plane_info';<\/pre>\n<p>Although the statement returns only a single value, it can still be difficult to read, especially if it\u2019s a complex compound statement. To view the statement in its entirety, right-click the value directly in the results and click <em>Open Value in Viewer<\/em>. Select Text if it\u2019s not already selected. MySQL launches a separate window that displays the value, as shown in Figure 4.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93861\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-description-automa-3.png\" alt=\"Image showing the Edit Data for ROUTINE_DEFINITION (TEXT) dialog. The definition of the stored procedure is shown\" width=\"808\" height=\"515\" \/><\/p>\n<p class=\"caption\">Figure 4. Examining the stored procedure\u2019s routine body in Viewer<\/p>\n<p>Of course, verifying that the stored procedure exists doesn\u2019t tell you whether it will work as expected. For this reason, you should also execute the procedure and see what sort of results it returns (in addition to running it through a proper QA cycle). For this, use a <code>CALL<\/code> statement that specifies the procedure name, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">CALL get_plane_info;<\/pre>\n<p>When you call the procedure, MySQL runs the stored routine and returns the statement results, which are shown in Figure 5.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93862\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-3.png\" alt=\"Image showing the results of the stored procedure call. Three rows are returned.\" width=\"503\" height=\"160\" \/><\/p>\n<p class=\"caption\">Figure 5. Viewing the results after calling the stored procedure<\/p>\n<p>As expected, the <code>CALL<\/code> statement returns the aggregated airplane data, grouped by manufacturer. These are the same results that you would have received if you ran the routine\u2019s <code>SELECT<\/code> statement directly. However, the statement is now persisted to the database, eliminating the need to write the statement at the application level.<\/p>\n<h2>Adding an input parameter to a stored procedure<\/h2>\n<p>The <code>get_plane_info<\/code> stored procedure created in the previous section demonstrated most of the main components that go into a MySQL stored procedure. In a production environment, the compound statement will likely be more complex, but this example still provides most of the basics. That said, one of the most beneficial aspects of a stored procedure is its ability to support input and output parameters.<\/p>\n<p>In this section, I demonstrate how to add an input parameter to the procedure definition. (I\u2019ll cover output parameters in the next section.) Before I get into that, you should be aware that you cannot simply alter a procedure definition like you can a table or view definition. You can modify a procedure\u2019s characteristics, but nothing more. To make any significant updates, you must drop the procedure and then re-create it, incorporating any new elements.<\/p>\n<p>To drop a stored procedure, you can use a <code>DROP<\/code> <code>PROCEDURE<\/code> statement, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DROP PROCEDURE IF EXISTS get_plane_info;<\/pre>\n<p>The <code>IF<\/code> <code>EXISTS<\/code> clause is optional, but it can help avoid unnecessary errors. After you run this statement, you can confirm that the procedure has been dropped by again querying the <code>routines<\/code> view in the <code>INFORMATION_SCHEMA<\/code> database:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT * FROM information_schema.routines\r\nWHERE routine_schema = 'travel';<\/pre>\n<p>The statement should now return an empty result set, unless you created other stored procedures or functions.<\/p>\n<p>After you delete the <code>get_plane_info<\/code> stored procedure, you can update your <code>CREATE<\/code> <code>PROCEDURE<\/code> statement to include an input parameter. For each parameter, you should specify the parameter type, the parameter name, and the parameter\u2019s data type. MySQL supports three parameter types:<\/p>\n<ul>\n<li><strong>IN.<\/strong> Input parameter that passes a value from the caller into the procedure\u2019s routine.<\/li>\n<li><strong>OUT.<\/strong> Output parameter that passes a value from the routine back to the caller.<\/li>\n<li><strong>INOUT.<\/strong> Parameter that can be initialized by the caller, updated by the routine, and then returned to the caller with its new value.<\/li>\n<\/ul>\n<p>The following <code>CREATE<\/code> <code>PROCEDURE<\/code> statement includes one input parameter, which is named <code>in_name<\/code> and defined with the <code>VARCHAR(50)<\/code> data type:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELIMITER \/\/\r\nCREATE PROCEDURE get_plane_info(\r\n  IN in_name VARCHAR(50))\r\nCOMMENT 'retrieves aggregated airplane information'\r\nBEGIN\r\n  SELECT a.manufacturer_id, m.manufacturer, \r\n    COUNT(*) AS plane_count,\r\n    ROUND(AVG(a.wingspan), 2) AS avg_span, \r\n    ROUND(AVG(a.plane_length), 2) AS avg_length\r\n  FROM airplanes a INNER JOIN manufacturers m\r\n    ON a.manufacturer_id = m.manufacturer_id\r\n  WHERE m.manufacturer = in_name;\r\nEND\/\/\r\nDELIMITER ;<\/pre>\n<p>The parameter definition is enclosed in parentheses and includes the <code>IN<\/code> keyword, parameter name, and data type. I also updated the <code>SELECT<\/code> statement to reflect the use of the parameter. It no longer includes the <code>GROUP<\/code> <code>BY<\/code> and <code>ORDER<\/code> <code>BY<\/code> clauses but now includes a <code>WHERE<\/code> clause that compares the <code>in_name<\/code> parameter to the <code>manufacturer<\/code> column. In this way, the caller can specify the manufacturer on which to base the query.<\/p>\n<p>The <code>CREATE<\/code> <code>PROCEDURE<\/code> statement also includes the <code>COMMENT<\/code> characteristic, which appends a comment to the procedure definition. You can include one or more characteristics in a <code>CREATE<\/code> <code>PROCEDURE<\/code> statement after the parameter definitions. A characteristic is one of several options that can be added to a procedure definition. Each characteristic affects the procedure definition in a different way. For example, this characteristic adds a comment, but you can also use characteristics to indicate the routine language, specify whether the routine is deterministic, or define the routine\u2019s nature.<\/p>\n<p>When calling a stored procedure that takes an input parameter, you must include the parameter value in parentheses. If it\u2019s a character value, you must enclose it in single quotes. For example, the following <code>CALL<\/code> statement specifies <code>piper<\/code> as the value for the procedure\u2019s input parameter:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">CALL get_plane_info ('piper');<\/pre>\n<p>When MySQL runs the procedure\u2019s routine, it substitutes the <code>piper<\/code> value for the <code>in_name<\/code> parameter specified in the <code>WHERE<\/code> clause. Figure 6 shows the results now returned by the stored procedure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93863\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-4.png\" alt=\"Image with results of stored procedure call. Only one row is returned \" width=\"521\" height=\"135\" \/><\/p>\n<p class=\"caption\">Figure 6. Calling a stored procedure with an input parameter<\/p>\n<p>When defining your stored procedure, you can include multiple <code>IN<\/code> parameters, separating them with commas. Then, when you call the procedure, you specify each parameter value within the parentheses, again separated with commas. You can also include <code>OUT<\/code> parameters or <code>INOUT<\/code> parameters alongside the input parameters.<\/p>\n<h2>Adding output parameters to a stored procedure<\/h2>\n<p>Now let\u2019s look at how to add multiple <code>OUT<\/code> parameters to the <code>get_plane_info<\/code> stored procedure. Output parameters provide a mechanism for returning one or more values back to the caller, rather than returning a single result set. For this example, you\u2019ll add five output parameters, which will correspond to the columns specified in the routine\u2019s <code>SELECT<\/code> list.<\/p>\n<p>To add the parameters, you\u2019ll need to again drop the procedure and then run an updated <code>CREATE<\/code> <code>PROCEDURE<\/code> statement. The output parameters are specified within the same parentheses as the input parameter, as shown in the following script:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DROP PROCEDURE IF EXISTS get_plane_info;\r\nDELIMITER \/\/\r\nCREATE PROCEDURE get_plane_info(\r\n  IN in_name VARCHAR(50),\r\n  OUT out_id INT UNSIGNED,\r\n  OUT out_name VARCHAR(50),\r\n  OUT plane_count SMALLINT UNSIGNED,\r\n  OUT avg_wingspan DECIMAL(5,2), \r\n  OUT avg_length DECIMAL(5,2))\r\nCOMMENT 'retrieves aggregated airplane information'\r\nBEGIN\r\n  SELECT a.manufacturer_id, m.manufacturer, \r\n    COUNT(*),\r\n    ROUND(AVG(a.wingspan), 2), \r\n    ROUND(AVG(a.plane_length), 2)\r\n  INTO out_id, out_name, plane_count, avg_wingspan, avg_length\r\n  FROM airplanes a INNER JOIN manufacturers m\r\n    ON a.manufacturer_id = m.manufacturer_id\r\n  WHERE m.manufacturer = in_name;\r\nEND\/\/\r\nDELIMITER ;<\/pre>\n<p>For each output parameter, you must specify the <code>OUT<\/code> keyword, the parameter name, and the parameter\u2019s data type. In addition, you must add an <code>INTO<\/code> clause after the <code>SELECT<\/code> list that returns the results to the output parameters. I\u2019ve also removed the column aliases from the <code>SELECT<\/code> list because they\u2019re no longer needed.<\/p>\n<p>When you call a stored procedure that returns output parameters, you can capture those parameter values by passing in a user-defined variable for each output parameter to hold its value, as shown in the following <code>CALL<\/code> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">CALL get_plane_info ('beechcraft', @out_id, @out_name, \r\n  @plane_count, @avg_wingspan, @avg_length);<\/pre>\n<p>The <code>CALL<\/code> statement specifies <code>beechcraft<\/code> as the input parameter value. This is followed by five user-defined variables, which correspond to the parameters specified in the stored procedure definition. When you run the <code>CALL<\/code> statement, the returned parameter values are assigned to the variables.<\/p>\n<p>The exact way in which you\u2019ll handle output parameters in your application will depend on the programming language that you\u2019re using. In the meantime, you can then verify that your variables contain the expected values by running a <code>SELECT<\/code> statement similar to the following:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT @out_id, @out_name, @plane_count, @avg_wingspan, @avg_length;<\/pre>\n<p>Figure 7 shows the results returned by the <code>SELECT<\/code> statement.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93864\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-5.png\" alt=\"Image showing the results of calling the stored procedure with Beechcraft\" width=\"562\" height=\"131\" \/><\/p>\n<p class=\"caption\">Figure 7. Viewing the routine\u2019s output parameter values for Beechcraft planes<\/p>\n<p>The figure shows the results when you specify <code>beechcraft<\/code> as the input value when calling the stored procedure. If you were to specify another value, such as <code>airbus<\/code>, your <code>SELECT<\/code> statement would return much different results, as shown in Figure 8.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93865\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-application-description-1.png\" alt=\"Image showing the results of the stored procedure called with Airbus\" width=\"556\" height=\"124\" \/><\/p>\n<p class=\"caption\">Figure 8. Viewing the routine\u2019s output parameter values for Airbus planes<\/p>\n<p>Both <code>IN<\/code> and <code>OUT<\/code> parameters can make stored procedures far more flexible when supporting data-driven applications. You might also encounter situations when you want to use an <code>INOUT<\/code> parameter. For example, you might create a stored procedure that includes some type of counter. You can use an <code>INOUT<\/code> parameter to set the counter\u2019s initial value and then return the new counter value based on the routine\u2019s output.<\/p>\n<h2>Altering a stored procedure in MySQL<\/h2>\n<p>MySQL supports the <code>ALTER<\/code> <code>PROCEDURE<\/code> statement for updating a procedure\u2019s characteristics. You cannot use this statement to make any other changes to a procedure definition. You are limited to characteristics only. For example, the following <code>ALTER<\/code> <code>PROCEDURE<\/code> statement adds two characteristics to the procedure definition, but the rest of the procedure definition will remain unchanged:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">ALTER PROCEDURE get_plane_info\r\nREADS SQL DATA\r\nSQL SECURITY INVOKER;<\/pre>\n<p>The <code>READS<\/code> <code>SQL<\/code> <code>DATA<\/code> characteristic indicates that the routine includes statements that read data. This type of characteristic is advisory only and does not constrain the routine in any way. The <code>SQL<\/code> <code>SECURITY<\/code> <code>INVOKER<\/code> characteristic indicates that the routine should run under the security context of the user account that invokes the routine rather than the definer account.<\/p>\n<p>After you run the <code>ALTER<\/code> <code>PROCEDURE<\/code> statement, you can verify that the characteristics have been added by viewing the procedure definition on the <em>Stored Procedure<\/em> tab, which is shown in Figure 9.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93866\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/graphical-user-interface-text-application-descr-6.png\" alt=\"Image showing the stored procedure in the edit dialog. The three characteristics can be seen: READS SQL DATA, SQL SECURITY INVOKER, COMMENT 'retrieves aggregated airplane information'\" width=\"810\" height=\"637\" \/><\/p>\n<p class=\"caption\">Figure 9. Viewing the procedure definition on the <em>Stored Procedure<\/em> tab<\/p>\n<p>Notice that the <code>CREATE<\/code> <code>PROCEDURE<\/code> statement now includes three characteristics: the two you just added and the original <code>COMMENT<\/code> characteristic that you added earlier.<\/p>\n<h2>Working with stored procedures in MySQL<\/h2>\n<p>Stored procedures can offer a great deal of flexibility, while helping to streamline application development. However, a procedure is only as effective as its underlying routine and the SQL statements it contains. In this exercise, I showed you how to create a stored procedure whose routine contained a single <code>SELECT<\/code> statement, but you can build routines that define far more complex logic. Later in the series, I\u2019ll be demonstrating how to create more robust compound statements that you can build into your stored procedures or use for other types of queries.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MySQL stored procedures are objects containing one or more SQL statements for reuse. They often contain business logic. Robert Sheldon demonstrates how to create and call stored procedures.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,145792],"tags":[95506],"coauthors":[6779],"class_list":["post-93857","post","type-post","status-publish","format-standard","hentry","category-featured","category-mysql","tag-automate"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93857","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=93857"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93857\/revisions"}],"predecessor-version":[{"id":93873,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93857\/revisions\/93873"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=93857"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=93857"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=93857"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=93857"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}