Lecture 05.4 SQL StoredProcedures 32
Lecture 05.4 SQL StoredProcedures 32
Stored Procedures
1 / 32
Stored Procedures
Refer Joel Murach, Murach’s MySQL, 2nd Edition, 2015, ISBN-13: 978-1890774820
3 / 32
Procedural Code to Display Data
--A stored procedure that displays a message
DELIMITER //
CREATE PROCEDURE test()
BEGIN
SELECT 'This is a test.' AS message;
END//
The response from the system
4 / 32
A Stored Procedure with Variables
DELIMITER //
CREATE PROCEDURE Stud_test(student_id_var INT)
BEGIN
DECLARE max_grade_var DECIMAL(9,2);
DECLARE min_grade_var DECIMAL(9,2);
DECLARE percent_difference_var DECIMAL(9,4);
DECLARE count_grade_var INT;
5 / 32
IF Statement in SQL
A stored procedure that uses an IF statement
The syntax of the IF statement DELIMITER //
IF boolean_expression THEN
statement_1; CREATE PROCEDURE test()
[statement_2;]... BEGIN
[ELSEIF boolean_expression THEN DECLARE first_invoice_due_date DATE;
statement_1;
[statement_2;]...]... SELECT MIN(invoice_due_date)
[ELSE INTO first_invoice_due_date
statement_1; FROM invoices
[statement_2;]...] WHERE invoice_total - payment_total - credit_total > 0;
END IF;
IF first_invoice_due_date < NOW() THEN
Note SELECT 'Outstanding invoices overdue!';
ELSEIF first_invoice_due_date = NOW() THEN
• You can also code parentheses around the SELECT 'Outstanding invoices are due today!';
ELSE
Boolean expressions in an IF statement. SELECT 'No invoices are overdue.';
END IF;
END//
6 / 32
CASE Statement in SQL Stored Procedure
A stored procedure that uses
a simple CASE statement
The syntax of the simple CASE statement DELIMITER //
CASE expression
WHEN expression_value_1 THEN CREATE PROCEDURE test()
statement_1; BEGIN
[statement_2;]... DECLARE terms_id_var INT;
[WHEN expression_value_2 THEN
statement_1; SELECT terms_id INTO terms_id_var
[statement_2;]...]... FROM invoices WHERE invoice_id = 4;
[ELSE
statement_1; CASE terms_id_var
[statement_2;]...] WHEN 1 THEN
END CASE; SELECT 'Net due 10 days' AS Terms;
WHEN 2 THEN
SELECT 'Net due 20 days' AS Terms;
WHEN 3 THEN
SELECT 'Net due 30 days' AS Terms;
ELSE
SELECT 'Net due more than 30 days' AS Terms;
END CASE;
END//
7 / 32
Coding a Loop in SQL: while loop
A stored procedure that uses a WHILE loop
DELIMITER //
The syntax of the WHILE loop
WHILE boolean_expression DO CREATE PROCEDURE test()
statement_1; BEGIN
[statement_2;]... DECLARE i INT DEFAULT 1;
END WHILE; DECLARE s VARCHAR(400) DEFAULT '';
WHILE i < 4 DO
SET s = CONCAT(s, 'i=', i, ' | ');
SET i = i + 1;
END WHILE;
SELECT s AS message;
END//
8 / 32
Coding a Loop in SQL: repeat and simple
loop
A simple loop
A REPEAT loop testLoop : LOOP
REPEAT SET s = CONCAT(s, 'i=', i, ' | ');
SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1;
SET i = i + 1;
UNTIL i = 4 IF i = 4 THEN
END REPEAT; LEAVE testLoop;
END IF;
END LOOP testLoop;
9 / 32
Cursor
A database cursor is an identifier associated with a group of rows associated after an operation.
It is, in a sense, a pointer to the current row in a buffer. You must use a cursor in the following
cases: Statements that return more than one row of data from the database server: A SELECT
statement requires a select cursor.
10 / 32
Cursors and SQL
SQL statements work with an entire result set rather than individual rows. However, you may sometimes
need to work with the data in a result set one row at a time. To do that, you can use a cursor.
The syntax for using a cursor
Declare a cursor
DECLARE cursor_name CURSOR FOR select_statement;
Declare an error handler for when no rows are found
in the cursor
DECLARE CONTINUE HANDLER FOR NOT FOUND handler_statement;
Open the cursor
OPEN cursor_name;
Get column values from the row and store them
in a series of variables
FETCH cursor_name INTO variable1
[, variable2][, variable3]...;
Close the cursor
CLOSE cursor_name;
11 / 32
Stored Procedure that uses a Cursor
DELIMITER //
CREATE PROCEDURE test()
BEGIN
DECLARE invoice_id_var INT;
DECLARE invoice_total_var DECIMAL(9,2); DECLARE Declaration of
row_not_found Select for the
DECLARE update_count
TINYINT DEFAULT FALSE; INT DEFAULT 0; cursor
DECLARE invoices_cursor CURSOR FOR
SELECT invoice_id, invoice_total
FROM invoices
WHERE invoice_total - payment_total - credit_total > 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND Fetch the row from
SET row_not_found = TRUE;
OPEN invoices_cursor; cursor
WHILE row_not_found = FALSE DO
FETCH invoices_cursor INTO invoice_id_var, invoice_total_var;
IF invoice_total_var > 1000 THEN
UPDATE invoices
SET credit_total = credit_total + (invoice_total * .1)
WHERE invoice_id = invoice_id_var;
SET update_count = update_count + 1; END IF;
END WHILE;
CLOSE invoices_cursor;
SELECT CONCAT(update_count, ' row(s) updated.‘);
END//
12 / 32
Errors can occur!
A stored procedure that doesn’t handle errors
DELIMITER //
13 / 32
SQL for Error Handling
• You can use the DECLARE...HANDLER statement to declare a handler for errors that may occur.
In MySQL, this is referred to as a condition handler.
• To continue execution when an error occurs, use the CONTINUE keyword. To exit the current
block of code when an error occurs, use the EXIT keyword.
• For a complete list of the MySQL error codes and their corresponding SQLSTATE codes, you can
search the MySQL Reference Manual for “Server error codes”
The syntax for declaring a condition handler -- declare a condition handler for a MySQL error code
DECLARE CONTINUE HANDLER FOR 1329
DECLARE {CONTINUE|EXIT} HANDLER SET row_not_found = TRUE
FOR {
mysql_error_code|
--declare a condition handler for a SQLSTATE code
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SQLSTATE sqlstate_code|
SET row_not_found = TRUE
named_condition
} --declare a condition handler for a named condition
handler_actions; DECLARE CONTINUE HANDLER FOR NOT FOUND
SET row_not_found = TRUE
14 / 32
Error Handling by mySQL
• Errors resulting from faulty code or missing
data are given an Error code by MySQL
• There are over 700 MySQL error codes
• Each error code corresponds with a SQLSTATE
code that’s part of the ANSI standard.
• MySQL provides 3 built-in named conditions
that correspond to most commonly occurring
error codes and SQLSTATE code combination
• Warning: Why? As an example, If you
declared a column to be an integer but are
attempting to insert a string, MySQL may
perform implicit type conversion and emit a
warning.
15 / 32
Stored Procedure that Uses Multiple Condition
Handlers
DELIMITER //
CREATE PROCEDURE test()
BEGIN
DECLARE duplicate_entry_for_key TINYINT DEFAULT FALSE;
DECLARE column_cannot_be_null TINYINT DEFAULT FALSE;
DECLARE sql_exception TINYINT DEFAULT FALSE;
BEGIN
DECLARE EXIT HANDLER FOR 1062
SET duplicate_entry_for_key = TRUE;
DECLARE EXIT HANDLER FOR 1048
SET column_cannot_be_null = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION If allowing MySQL to continue to execute
SET sql_exception = TRUE;
statements in the block causes problems, you
INSERT INTO general_ledger_accounts VALUES (NULL, 'Test');
SELECT '1 row was inserted.' AS message;
should use an EXIT handler.
END;
IF duplicate_entry_for_key = TRUE THEN
SELECT 'Row was not inserted - duplicate key encountered.'
AS message;
ELSEIF column_cannot_be_null = TRUE THEN
SELECT 'Row was not inserted - column cannot be null.'
AS message;
ELSEIF sql_exception = TRUE THEN
SHOW ERRORS;
END IF;
END//
16 / 32
Input and Output Parameters for Stored
Procedures
• Input parameters accept values that are passed from the calling program. These values cannot
be changed by the body of the stored procedure. By default, parameters are defined as input
parameters. As a result, the IN keyword is optional for identifying input parameters.
•Output parameters store values that are passed back to the calling program. These values must
be set by the body of the stored procedure. To identify an output parameter, you must code
the OUT keyword.
•Input-output parameters can store an initial value that’s passed from the calling program.
However, the body of the stored procedure can change this parameter. To identify an
input-output parameter, you must code the INOUT keyword.
• When you work with output parameters or input/output parameters, the calling program
typically passes a user variable to the parameter list.
17 / 32
Example: Stored Procedure that Updates
a Table
DELIMITER //
CREATE PROCEDURE update_invoices_credit_total --A statement that calls the stored procedure
( CALL update_invoices_credit_total(56, 300);
invoice_id_param, credit_total_param
)
BEGIN
DECLARE sql_error TINYINT DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SET sql_error = TRUE;
START TRANSACTION;
UPDATE invoices
SET credit_total = credit_total_param
WHERE invoice_id = invoice_id_param;
IF sql_error = FALSE THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END//
18 / 32
Example: Using In-Out Parameters
DELIMITER //
CREATE PROCEDURE update_invoices_credit_total
(
IN invoice_id_param INT, --Script that calls the stored procedure and uses the output
IN credit_total_param DECIMAL(9,2), parameter
OUT update_count INT
) CALL update_invoices_credit_total(56, 200, @row_count);
BEGIN SELECT CONCAT('row_count: ', @row_count) AS update_count;
◦ DECLARE sql_error TINYINT DEFAULT FALSE;
◦ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
◦ SET sql_error = TRUE; START TRANSACTION;
◦ UPDATE invoices
◦ SET credit_total = credit_total_param
◦ WHERE invoice_id = invoice_id_param;
◦ IF sql_error = FALSE THEN
◦ SET update_count = 1;
◦ COMMIT;
◦ ELSE
◦ SET update_count = 0;
◦ ROLLBACK;
◦ END IF;
END//
19 / 32
Parameter Validation and Error Handling
• It’s generally considered a good practice to prevent errors by checking the parameters before they’re used to
make sure they’re valid
• This is often referred to as data validation
• Then, if the data isn’t valid, you can execute code that makes it valid, or you can raise an error, which returns the error
to the calling program
• To raise an error, you code the SIGNAL statement followed by the SQLSTATE keyword, followed by a SQLSTATE code
• The SIGNAL statement raises an error. When you raise an error, you must specify a SQLSTATE code
• Then, you can optionally include a SET statement that sets a message and MySQL error code for the error
• When you raise an error, MySQL returns the error to the caller in the same way that it returns errors that are raised by
the database engine. Then, the calling program can handle the error.
• If the calling program doesn’t catch this error, the system displays an error message
• If the calling program catches this error, it can include code that handles the error
• For example, the calling program can handle the error by printing a user-friendly message to the user and asking the user to input data again
20 / 32
Example: Parameter Validation and Error
Handling
--stored procedure that raises a predefined exception
DELIMITER // --A statement that calls the procedure
CREATE PROCEDURE update_invoices_credit_total
( CALL update_invoices_credit_total(56, -100);
invoice_id_param INT,
credit_total_param DECIMAL(9,2)
)
BEGIN The response from the system
◦ --Validate paramater values Error Code: 1264.
◦ IF credit_total_param < 0 THEN
◦ SIGNAL SQLSTATE '22003' The credit_total column must be greater than or equal to 0.
◦ SET MESSAGE_TEXT =
◦ 'The credit_total column must be greater than or equal to 0.',
◦ MYSQL_ERRNO = 1264;
◦ ELSEIF credit_total_param >= 1000 THEN
◦ SIGNAL SQLSTATE '22003'
◦ SET MESSAGE_TEXT =
◦ 'The credit_total column must be less than 1000.',
◦ MYSQL_ERRNO = 1264;
◦ END IF;
◦ --Set default values for parameters IF credit_total_param IS NULL THEN SET credit_total_param = 100; END IF;
◦ UPDATE invoices
◦ SET credit_total = credit_total_param
◦ WHERE invoice_id = invoice_id_param;
END//
21 / 32
Stored Procedure that Inserts Data into a
Table After Validating Parameters
A procedure that validates data
DELIMITER //
22 / 32
Stored Procedure that Inserts Data into a
Table After Validating Parameters
A procedure that validates data (continued)
-- Validate paramater values
IF invoice_total_param < 0 THEN
SIGNAL SQLSTATE '22003'
SET MESSAGE_TEXT =
'The invoice_total column must be a positive number.',
MYSQL_ERRNO = 1264;
ELSEIF invoice_total_param >= 1000000 THEN
SIGNAL SQLSTATE '22003'
SET MESSAGE_TEXT =
'The invoice_total column must be less than 1,000,000.',
MYSQL_ERRNO = 1264;
END IF;
23 / 32
Stored Procedure that Inserts Data into a
Table After Validating Parameters
A procedure that validates data (continued)
IF invoice_due_date_param IS NULL THEN
SELECT terms_due_days INTO terms_due_days_var
FROM terms WHERE terms_id = terms_id_var;
SELECT DATE_ADD(invoice_date_param,
INTERVAL terms_due_days_var DAY)
INTO invoice_due_date_var;
ELSE
SET invoice_due_date_var = invoice_due_date_param;
END IF;
24 / 32
Stored Procedure that Inserts Data into a
Table After Validating Parameters
Two statements that call the stored procedure
CALL insert_invoice(34, 'ZXA-080', '2015-01-18',
14092.59, 3, '2015-03-18'); • If the data for each of the columns of the row is
CALL insert_invoice(34, 'ZXA-082', '2015-01-18',
valid, the procedure executes an INSERT
14092.59, NULL, NULL); statement to insert the row. Otherwise, the
procedure or database engine raises an error
The message for a successful insert
1 row(s) affected
and exits the procedure.
• If an application program calls this procedure, it
can handle any errors that are raised by the
A statement that raises an error procedure or by the database engine.
CALL insert_invoice(34, 'ZXA-080', '30-AUG-08',
-14092.59);
25 / 32
User Variables
• A user variable is a special type of MySQL variable that’s globally available to the current user.
• A user variable is only available to the current user and cannot be seen or accessed by other
users.
• A user variable is available as long as the user remains connected to the server, but it is reset
when the user disconnects.
• A user variable can store various data types including string, numeric, and date/time types.
However, you don’t have to declare a data type for a user variable.
• A user variable is available from statements coded both inside and outside of stored programs.
26 / 32
How to use User Variables
--The syntax for setting a user variable
SET @variable_name = expression
--Two stored procedures that work with the same user variable
DELIMITER //
CREATE PROCEDURE set_global_count
(
count_var INT
)
BEGIN
SET @count = count_var;
END//
CREATE PROCEDURE increment_global_count()
BEGIN
SET @count = @count + 1;
END//
--Two statements that call these stored procedures
CALL set_global_count(100);
CALL increment_global_count();
27 / 32
Stored Procedures and Dynamic SQL
• Dynamic SQL allows you to use procedural code to build and execute a SQL statement that
depends on parameters that aren’t known until runtime
• To do that, you can build a string that contains the SQL statement
• Then, you can use the PREPARE, EXECUTE, and DEALLOCATE statements to execute the statement
contained in the string
• Dynamic SQL is often used to build complex WHERE clauses that depend on multiple search
conditions that may or may not be specified by the user
28 / 32
Example: Dynamic SQL
A stored procedure that uses dynamic SQL (cont.)
IF min_invoice_total_param IS NOT NULL THEN
A stored procedure that uses dynamic SQL IF where_clause != "WHERE " THEN
DELIMITER // SET where_clause = CONCAT(where_clause, "AND ");
END IF;
CREATE PROCEDURE select_invoices
( SET where_clause = CONCAT(where_clause,
min_invoice_date_param DATE, "invoice_total > ", min_invoice_total_param);
min_invoice_total_param DECIMAL(9,2) END IF;
)
BEGIN IF where_clause = "WHERE " THEN
DECLARE select_clause VARCHAR(200);
DECLARE where_clause VARCHAR(200); SET @dynamic_sql = select_clause;
ELSE
SET select_clause = "SELECT invoice_id, invoice_number, SET @dynamic_sql = CONCAT(select_clause, where_clause);
invoice_date, invoice_total END IF;
FROM invoices ";
SET where_clause = "WHERE ";
PREPARE select_invoices_statement
IF min_invoice_date_param IS NOT NULL THEN FROM @dynamic_sql;
SET where_clause = CONCAT(where_clause,
" invoice_date > '", min_invoice_date_param, "'"); EXECUTE select_invoices_statement;
END IF;
DEALLOCATE PREPARE select_invoices_statement;
END//
29 / 32
Example: Dynamic SQL output
30 / 32
How to Drop a Stored Procedure
• If you attempt to drop a stored procedure that doesn’t exist, MySQL returns an error.
• To prevent this error, you can add the optional IF EXISTS keywords to the DROP PROCEDURE statement
as shown by the third example.
• Then, MySQL only attempts to drop the stored procedure if it exists.
•If you drop a table or view used by a procedure, you should be sure to drop the procedure as
well.
• If you don’t, the procedure can still be called by any user or program that has been granted the
appropriate privileges.
• Then, an error will occur because the table or view that the procedure depends on no longer exists.
31 / 32
How to Drop a Stored Procedure
The syntax of the DROP PROCEDURE statement
DROP PROCEDURE [IF EXISTS] procedure_name
32 / 32