0% found this document useful (0 votes)
16 views

Lecture 05.4 SQL StoredProcedures 32

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views

Lecture 05.4 SQL StoredProcedures 32

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 32

Stored Programs

Stored Procedures

1 / 32
Stored Procedures

Refer Joel Murach, Murach’s MySQL, 2nd Edition, 2015, ISBN-13: 978-1890774820

Murach's MySQL, C15 © 2015, MIKE MURACH & ASSOCIATES, INC. 2 / 32


Stored Procedures Overview
• A stored procedure, which can also be referred to as a sproc or just a procedure, is a database object that
contains a block of procedural SQL code
• You can use stored procedures to modify the data that’s stored within a database.
• For example, you can use a stored procedure to execute an INSERT, UPDATE, or DELETE statement
CREATE PROCEDURE procedure_name
• After the name of the procedure, you code a set of parentheses ( [parameter_name_1 data_type][,
• Within the parentheses, you can code one or more parameters (and datatypes) for the procedure
• A parameter is typically used to pass a value to the stored procedure from a calling program parameter_name_2 data_type]...)
• After the parentheses, you code a block of statements {
• This block is identified by the BEGIN and END keywords … code block
• Within the block, you can code most SQL statements
}
• When you run the CREATE PROCEDURE statement, MySQL compiles the code for the procedure and stores the
compiled code in the database.
• As part of this process, MySQL’s compiler checks the syntax of the code within the procedure
• If you’ve made a coding error, the system responds with an appropriate message and the procedure isn’t created
• You can execute, or call, a stored procedure by using the CALL statement
• When you use the CALL statement, you must pass parameters by position
• In other words, you must code the parameters in the same order as they are coded in the CREATE PROCEDURE statement
• Stored procedures can be written to create objects that perform all kinds of data manipulations
• You can then grant privileges for users to execute these stored procedures – a good way to secure data and prevent both
accidental and malicious data manipulation/damage

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;

SELECT MAX(Grade), MIN(Grade), COUNT(StudentID) INTO


max_grade_var, min_grade_var, count_grade_var FROM
student_schedules WHERE ClassStatus =2 and StudentID = student_id_var;

SET percent_difference_var = (max_grade_var - min_grade_var) / min_grade_var * 100;

SELECT student_id_var as 'Student ID', CONCAT(max_grade_var) AS 'Maximum Grade’,


CONCAT( min_grade_var) AS 'Minimum Grade', CONCAT('%', ROUND(percent_difference_var, 2))
AS 'Percent difference', count_grade_var AS 'Number of Courses’;
END//
Call Stud_test (1001);

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;

• Use the LEAVE statement to go to the end of a loop.


• Use the ITERATE statement to go to the beginning of a loop

9 / 32
Cursor

Cursor is a Temporary table. It is Allocated by Database Server at the Time of Performing


DML(Data Manipulation Language) operations on Table by User. Cursors are used to store
Database Tables.

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 //

CREATE PROCEDURE test()


BEGIN
INSERT INTO general_ledger_accounts
VALUES (130, 'Cash');

SELECT '1 row was inserted.';


END//

The response from the system


Error Code: 1062. Duplicate entry 'Cash' for key
'account_description'

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 //

CREATE PROCEDURE insert_invoice


(
vendor_id_param INT,
invoice_number_param VARCHAR(50),
invoice_date_param DATE,
invoice_total_param DECIMAL(9,2),
terms_id_param INT,
invoice_due_date_param DATE
)
BEGIN
DECLARE terms_id_var INT;
DECLARE invoice_due_date_var DATE;
DECLARE terms_due_days_var INT;

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;

-- Set default values for parameters


IF terms_id_param IS NULL THEN
SELECT default_terms_id INTO terms_id_var
FROM vendors WHERE vendor_id = vendor_id_param;
ELSE
SET terms_id_var = terms_id_param;
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;

INSERT INTO invoices


(vendor_id, invoice_number, invoice_date,
invoice_total, terms_id, invoice_due_date)
VALUES (vendor_id_param, invoice_number_param, invoice_date_param,
invoice_total_param, terms_id_var, invoice_due_date_var);
END//

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);

The message when a validation error occurs


Error Code: 1264.
The invoice_total column must be a positive number.

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();

--A SELECT statement that directly accesses the user variable


SELECT @count AS count_var

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

A @dynamic_sql variable at runtime


with parameters inserted
SELECT invoice_id, invoice_number, invoice_date,
invoice_total
FROM invoices
WHERE invoice_date > '2014-08-29' AND invoice_total > 100

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

A statement that creates a stored procedure


DELIMITER //

CREATE PROCEDURE clear_invoices_credit_total


(
invoice_id_param INT
)
BEGIN
UPDATE invoices
SET credit_total = 0 A statement that drops the stored procedure
WHERE invoice_id = invoice_id_param; DROP PROCEDURE clear_invoices_credit_total
END//
A statement that drops the stored procedure
only if it exists
DROP PROCEDURE IF EXISTS clear_invoices_credit_total

32 / 32

You might also like