18.5. Creating Stored Routines


This section covers general syntax for stored routine definitions. The sections that follow detail specific statements that are used within routines.

To define a stored procedure or function, use a CREATE PROCEDURE or CREATE FUNCTION statement, respectively. These statements have the following syntax:

 CREATE PROCEDURE proc_name ([parameters])   [characteristics]   routine_body CREATE FUNCTION func_name ([parameters])   RETURNS data_type   [characteristics]   routine_body 

The routine name can be unqualified to create the routine in the default database, or qualified with a database name to create the routine in a specific database.

The database that a routine belongs to is used to interpret unqualified references to tables that occur within the routine definition. To refer to tables in other databases, use qualified names of the form db_name.table_name.

The optional parameter list declares the parameters that are to be passed to the routine when it is invoked. For details, see Section 18.5.2, "Declaring Parameters."

The optional characteristics clause contains one or more of the following values, which can appear in any order:

  • SQL SECURITY {DEFINER | INVOKER}

    A stored routine runs either with the privileges of the user who created it or the user who invoked it. The choice of which set of privileges to use is controlled by the value of the SQL SECURITY characteristic:

    • A value of DEFINER causes the routine to have the privileges of the user who created it. This is the default value.

    • A value of INVOKER causes the routine to run with the privileges of its invoking user. This means the routine has access to database objects only if that user can already access them otherwise.

    SQL SECURITY DEFINER enables you to create routines that access information on behalf of users who otherwise would not be able to do so. For example, a summary of financial data might be generated from tables in a business database. If you want to allow users to generate the summary but not have direct access to the contents of the underlying tables, create a procedure that produces the summary as a result set and define it to have DEFINER security. On the other hand, you must be careful not to create a DEFINER routine if you really do not want other users to have your privileges while the routine executes.

  • DETERMINISTIC or NOT DETERMINISTIC

    Indicates whether the routine always produces the same result when invoked with a given set of input parameter values. If it does not, the routine is non-deterministic. For example, a function that returns the country name corresponding to a country code is deterministic, but a function that returns a summary of financial data is non-deterministic because it returns different results as the information changes over time. This characteristic is intended to convey information to the query optimizer, but the optimizer does not use it as of this writing. If neither value is specified, the default is NOT DETERMINISTIC.

  • LANGUAGE SQL

    Indicates the language in which the routine is written. Currently, the only supported language is SQL, so SQL is the only allowable value for the LANGUAGE characteristic (and it is also the default value).

  • COMMENT 'string'

    Specifies a descriptive string for the routine. The string is displayed by the statements that return information about routine definitions. See Section 18.9, "Obtaining Stored Routine Metadata."

routine_body specifies the body of the procedure or function. This is the code to be executed when the routine is executed. A routine body consists of a single statement. However, this imposes no limitation on creating complex routines, because you can use a BEGIN ... END block for the routine body, and place multiple statements within the block. See Section 18.5.1, "Compound Statements."

The remarks made thus far about routine syntax are true both for procedures and functions. There are a few ways in which the syntax for creating a function differs from that for procedures:

  • Parameter declarations are not quite the same.

  • A function must include a RETURNS clause to indicate the data type of the value that the function returns. The type is any valid MySQL data type, such as INT, DECIMAL(10,2), or VARCHAR(40).

  • Somewhere in the body of the function there must be a RETURN statement to return a value to the caller.

The following listing shows an example procedure and function:

 CREATE PROCEDURE rect_area (width INT, height INT)   SELECT width * height AS area; CREATE FUNCTION circle_area (radius FLOAT)   RETURNS FLOAT   RETURN PI() * radius * radius; 

RETURNS is not terminated by a semicolon because it is just a clause, not a statement.

When a stored routine executes, its environment is set so that the database that it belongs to becomes its default database for the duration of its execution. Also, the sql_mode system variable value in effect when the routine executes is the value that was current when it was defined. The privileges of the routine are determined by its SQL SECURITY characteristic.

18.5.1. Compound Statements

Stored routine syntax requires that a routine body be a single statement. For a complex routine, you can satisfy this requirement by using a compound statement that contains other statements. A compound statement begins and ends with the BEGIN and END keywords and creates a block. In between BEGIN and END, write the statements that make up the block, each terminated by a semicolon character (';'). The BEGIN/END block itself is terminated by a semicolon, but BEGIN is not.

Here is a simple stored procedure that uses a compound statement containing several SELECT statements. It displays the number of records in tables from the world database:

 CREATE PROCEDURE world_record_count () BEGIN   SELECT 'Country', COUNT(*) FROM Country;   SELECT 'City', COUNT(*) FROM City;   SELECT 'CountryLanguage', COUNT(*) FROM CountryLanguage; END; 

The use of semicolon statement terminators in a multiple-statement definition is proble-matic if the program you are using to define the routine also treats semicolon as special. Section 18.4, "Defining Stored Routines," discusses how to deal with this issue when using the mysql program.

A block can be labeled, which is useful when it's necessary to alter the flow of control. For example, the LEAVE statement can be used to exit a labeled BEGIN/END block. The syntax for labeling a block looks like this:

 [label:] BEGIN ... END [label] 

Labels are optional, but the label at the end can be present only if the label at the beginning is also present, and the end label must have the same name as the beginning label. An end label is never required but can make a routine easier to understand because it helps a reader find the end of the labeled construct.

Blocks can be nested. In other words, a BEGIN/END block can contain other BEGIN/END blocks. Here is an example that uses nested blocks where the inner block is labeled. The LEAVE statement transfers control to the end of the labeled block if the expression evaluated by the IF statement is true:

 BEGIN   inner_block: BEGIN     IF DAYNAME(NOW()) = 'Wednesday' THEN       LEAVE inner_block;     END IF;     SELECT 'Today is not Wednesday';   END inner_block; END; 

In an inner block, you cannot use a label that has already been used for an outer block. The label name would be ambiguous for an attempt to transfer control to the label from within the inner block.

Labels can also be given for the LOOP, REPEAT, and WHILE loop-constructor statements; they follow the same labeling rules as for BEGIN/END. These statements are described in Section 18.5.8, "Flow Control."

18.5.2. Declaring Parameters

A stored routine definition can include parameter declarations. Parameters enable you to pass values to the routine when you invoke it. They are useful because you can write routines in a more general fashion, rather than hard-coding specific data values into them. For example, a parameter can identify the employee whose personnel record should be updated or the amount of money to be transferred in a financial transaction. You can invoke the same routine with different parameter values to produce different results. For procedures, parameters also enable you to pass information back from the procedure to the caller.

Here is a simple function that takes two DECIMAL parameters representing the cost for a taxable item and a tax rate, returns the amount of tax to be paid on the item:

 CREATE FUNCTION tax (cost DECIMAL(10,2), tax_rate DECIMAL(10,2))   RETURNS DECIMAL(10,4)   RETURN cost * tax_rate; 

Parameter declarations occur within the parentheses that follow the routine name in a CREATE PROCEDURE or CREATE FUNCTION statement. If there are multiple parameters, separate them by commas. A parameter declaration includes the parameter name and data type, to identify the name of the parameter within the routine and the kind of value it contains. Each parameter must have a different name. Parameter names are not case sensitive.

For procedures (but not functions), the name in a parameter declaration may be preceded by one of the following keywords to indicate the direction in which information flows through the parameter:

  • IN indicates an input parameter. The parameter value is passed in from the caller to the procedure. The procedure can assign a different value to the parameter, but the change is visible only within the procedure, not to the caller.

  • OUT indicates an output parameter. The caller passes a variable as the parameter. Any value the parameter has when it is passed is ignored by the procedure, and its initial value within the procedure is NULL. The procedure sets its value, and after the procedure terminates, the parameter value is passed back from the procedure to the caller. The caller sees that value when it accesses the variable.

  • INOUT indicates a "two-way" parameter that can be used both for input and for output. The value passed by the caller is the parameter's initial value within the procedure. If the procedure changes the parameter value, that value is seen by the caller after the procedure terminates.

If no keyword is given before a procedure parameter name, it is an IN parameter by default.

Parameters for stored functions are not preceded by IN, OUT, or INOUT. All function parameters are treated as IN parameters.

The following example demonstrates how the different procedure parameter types work. It shows which parameter values passed to a procedure are visible within the procedure, and which of the parameter values changed by the procedure are visible to the caller after the procedure terminates. First, define a procedure with one parameter of each type that displays the initial values of its parameters and then reassigns them before terminating:

 CREATE PROCEDURE param_test (IN p_in INT,                              OUT p_out INT,                              INOUT p_inout INT) BEGIN   SELECT p_in, p_out, p_inout;   SET p_in = 100, p_out = 200, p_inout = 300; END; 

Then assign values to three user variables and pass them to the param_test() procedure. The output from param_test() indicates that the original value of the output parameter is not visible within the procedure:

 mysql> SET @v_in = 0, @v_out = 0, @v_inout = 0; Query OK, 0 rows affected (0.00 sec) mysql> CALL param_test(@v_in, @v_out, @v_inout); +------+-------+---------+ | p_in | p_out | p_inout | +------+-------+---------+ |    0 |  NULL |       0 | +------+-------+---------+ 1 row in set (0.01 sec) Query OK, 0 rows affected (0.02 sec) 

After param_test() terminates, display the variable values. This output indicates that changes to the OUT and INOUT parameters (but not to the IN parameter) have been passed back to and are visible to the caller:

 mysql> SELECT @v_in, @v_out, @v_inout; +-------+--------+----------+ | @v_in | @v_out | @v_inout | +-------+--------+----------+ | 0     | 200    | 300      | +-------+--------+----------+ 1 row in set (0.01 sec) 

Parameters to stored routines need not be passed as user variables. They can be given as constants or expressions as well. However, for OUT or INOUT procedure parameters, if you do not pass a variable, the value passed back from a procedure will not be accessible.

18.5.3. The DECLARE Statement

The DECLARE statement is used for declaring several types of items in stored routines:

  • Local variables

  • Conditions, such as warnings or exceptions

  • Handlers for conditions

  • Cursors for accessing result sets row by row

DECLARE statements can be used only within a BEGIN/END block and must appear in the block before any other statements. If used to declare several types of items within a block, the DECLARE statements must appear in a particular order: You must declare variables and conditions first, then cursors, and finally handlers.

Each variable declared within a block must have a different name. This restriction also applies to declarations for conditions and for cursors. However, items of different types within a block can have the same name. Item names are not case sensitive.

Variables, conditions, handlers, and cursors created by DECLARE statements are local to the block. That is, they are valid only within the block (or any nested blocks). When a block terminates, any cursors still open are closed and all items declared within the block go out of scope and are no longer accessible.

If an inner block contains an item that has the same name as the same kind of item in an outer block, the outer item cannot be accessed within the inner block.

The following sections describe how to use DECLARE for each type of item, although the sections do not appear in the same order in which items must be declared. (Conditions and handlers are related, so they appear in the same section.)

18.5.4. Variables in Stored Routines

To declare local variables for use within a block, use a DECLARE statement that specifies one or more variable names, a data type, and optionally a default value:

 DECLARE var_name [, var_name] ... data_type [DEFAULT value] 

Each variable named in the statement has the given data type (and default value, if the DEFAULT clause is present). To declare variables that have different data types or default values, use separate DECLARE statements. The initial value is NULL for variables declared with no DEFAULT clause.

Each variable declared within a block must have a different name.

A variable may be assigned a value using a SET, SELECT ... INTO, or FETCH ... INTO statement. The variable's value can be accessed by using it in an expression. The following sections provide examples.

To avoid name clashes, it is best not to give a local variable the same name as any table columns that you refer to within a routine. For example, if you select the Code column from the Country table, declaring a local variable with a name of Code leads to ambiguity. A naming convention can be helpful here. For example, you could name the variable as code_var. The same principle can be used for routine parameters, by using names such as code_param.

Local routine variable names are not written with a leading '@' character. This differs from the @var_name syntax used for writing user variables.

18.5.4.1 Assigning Variable Values with SET

The SET statement assigns values to variables. These can be system or user variables, just as when you use SET outside the context of stored routines. However, within a stored routine, SET also can refer to local variables that were declared previously with DECLARE.

A SET statement can perform a single assignment or multiple assignments. For example:

 DECLARE var1, var2, var3 INT; SET var1 = 1, var2 = 2; SET var3 = var1 + var2; 

18.5.4.2 Assigning Variable Values with SELECT ... INTO

The SELECT ... INTO statement assigns the result of a SELECT statement to variables. Outside the context of stored routines, these must be user variables. Within stored routines, the statement also can be used to assign values to local variables that were declared pre-viously with DECLARE. The following statements declare two variables and select into them the country name and population corresponding to a given country code:

 DECLARE name_var CHAR(52); DECLARE pop_var INT; SELECT Name, Population INTO name_var, pop_var   FROM Country WHERE Code = 'ESP'; 

The SELECT statement must select at most a single row. If it selects more than one row, an error occurs. If the statement selects no rows, the variables following the INTO keyword remain unchanged.

SELECT ... INTO can also assign values to routine parameters. If a parameter is an INOUT or OUT parameter, the value assigned to it is passed back to the caller.

18.5.5. Conditions and Handlers

A handler has a name and a statement to be executed upon occurrence of a given condition such as a warning or an error. Handlers commonly are used for detecting problems and dealing with them in a more appropriate way than simply having the routine terminate with an error.

The following example demonstrates one way to use a handler. It's based on a table, unique_names, that holds unique names, and another table, dup_names, that holds rows that could not be inserted into unique_names. To do this, it's necessary to be able to handle duplicate-key errors when inserting into unique_names and insert into dup_name instead.

The tables are defined as follows, where the only difference is that unique_names has a unique key constraint:

 CREATE TABLE unique_names (     name CHAR(20) NOT NULL PRIMARY KEY ); CREATE TABLE dup_names (     name CHAR(20) NOT NULL ); 

The following procedure, add_name(), takes a name as its parameter and attempts to insert it into the unique_name table. If that fails because the name is a duplicate, an error with SQLSTATE value 23000 occurs. The routine includes a handler for this condition, and associates it with a block that inserts the name into the dup_key table instead:

 CREATE PROCEDURE add_name (name_param CHAR(20)) BEGIN   DECLARE EXIT HANDLER FOR SQLSTATE '23000'   BEGIN     INSERT INTO dup_names (name) VALUES(name_param);     SELECT 'duplicate key found, inserted into dup_names' AS result;   END;   INSERT INTO unique_names (, name) VALUES(name_param);   SELECT 'row inserted successfully into unique_names' AS result; END; 

If we invoke the routine with the same name twice, it produces the following result:

 mysql> CALL add_name ('my name'); +---------------------------------------------+ | result                                      | +---------------------------------------------+ | row inserted successfully into unique_names | +---------------------------------------------+ mysql> CALL add_name ('my name'); +----------------------------------------------+ | result                                       | +----------------------------------------------+ | duplicate key found, inserted into dup_names | +----------------------------------------------+ 

It is allowable but not necessary to give a name to a condition by declaring it. You might declare a condition to provide a name for it that is more meaningful than the code that it stands for. For example, the following declaration associates the descriptive name dup_key with the SQLSTATE value '23000', which is returned whenever the MySQL server encounters a duplicate-key error:

 DECLARE dup_key CONDITION FOR SQLSTATE '23000'; 

Named conditions and handlers are both declared with DECLARE statements. Conditions must be declared along with variables before any cursor or handler declarations. Handler declarations must follow declarations for variables, conditions, and cursors.

To name a condition, use a DECLARE CONDITION statement:

 DECLARE condition_name CONDITION FOR condition_type 

Each condition declared within a block must have a different name. After you declare a condition, you can refer to it by name in a DECLARE HANDLER statement.

A condition type can be an SQLSTATE value, specified as SQLSTATE (or SQLSTATE VALUE) followed by a five-character string literal. MySQL extends this to allow numeric MySQL error codes as well. The following declarations are equivalent. Each declares a condition named null_not_allowed for the error that occurs for an attempt to assign NULL when NULL is not allowed:

 DECLARE null_not_allowed CONDITION FOR SQLSTATE '23000'; DECLARE null_not_allowed CONDITION FOR 1048; 

The DECLARE HANDLER statement creates a handler for one or more conditions and associates them with an SQL statement that will be executed should any of the conditions occur when the routine is run:

 DECLARE handler_type HANDLER FOR   condition_type [, condition_type] ...   statement 

The handler type indicates what happens once the handler statement is executed. CONTINUE causes routine execution to continue; the SQL statement that follows the statement in which the condition occurred is the next to be processed. EXIT causes control to transfer to the end of the block in which the handler is declared; the intermediate SQL statements are not processed. Standard SQL defines UNDO handlers as well, but MySQL does not currently support them.

Each condition associated with a handler must be one of the following:

  • An SQLSTATE value or MySQL error code, specified the same way as in a DECLARE CONDITION statement

  • A condition name declared previously with a DECLARE CONDITION statement

  • SQLWARNING, which handles conditions for all SQLSTATE values that begin with 01

  • NOT FOUND, which handles conditions for all SQLSTATE values that begin with 02

  • SQLEXCEPTION, which handles conditions for all SQLSTATE values not handled by SQLWARNING or NOT FOUND

The statement at the end of DECLARE HANDLER specifies the statement to execute when a handled condition occurs. It can be a simple statement or a compound statement:

 DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET exit_loop = 1; DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'   BEGIN     statement_list   END; 

To ignore a condition, declare a CONTINUE handler for it and associate it with an empty block:

 DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END; 

18.5.6. Cursors

A cursor enables you to access a result set one row at a time. Because of this row orientation, cursors often are used in loops that fetch and process a row within each iteration of the loop.

The cursor implementation in MySQL has the following properties: It provides for read-only cursors; they cannot be used to modify tables. Cursors also only advance through a result row by row; that is, they are not scrollable.

To use a cursor in a stored routine, begin by writing a DECLARE CURSOR statement that names the cursor and associates it with a SELECT statement that produces a result set:

 DECLARE cursor_name CURSOR FOR select_statement 

Each cursor declared within a block must have a different name.

To open the cursor, name it in an OPEN statement. This executes the SELECT statement associated with the cursor:

 OPEN cursor_name 

The FETCH statement fetches the next row of an open cursor's result set. The statement names the cursor and provides a list of variables into which to fetch row column values. There must be one variable per column in the result set. You can fetch values into local variables or routine parameters:

 FETCH cursor_name INTO var_name [, var_name] ... 

FETCH often occurs in a loop so that all rows in the result set can be processed. That raises an issue: What happens when you reach the end of the result? The answer is that a No Data condition occurs (SQLSTATE 02000), which you can detect by declaring a handler for that condition. For example:

 DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' statement; 

When you are done with the cursor, close it with a CLOSE statement:

 CLOSE cursor_name 

Closing a cursor is optional. Any cursors declared in a block are closed automatically if they are open when the block terminates.

The following example shows how to use each of the cursor-related statements just discussed. The example declares a cursor named c and associates it with a statement that selects rows for African countries in the Country table. It also declares a condition handler that detects the end of the result set. (The handler statement is empty because the only purpose for the handler is to transfer control to the end of its enclosing block.)

 BEGIN   DECLARE row_count INT DEFAULT 0;   DECLARE code_var CHAR(3);   DECLARE name_var CHAR(52);   DECLARE c CURSOR FOR     SELECT Code, Name FROM Country WHERE Continent = 'Africa';   OPEN c;   BEGIN     DECLARE EXIT HANDLER FOR SQLSTATE '02000' BEGIN END;     LOOP       FETCH c INTO code_var, name_var;       SET row_count = row_count + 1;     END LOOP;   END;   CLOSE c;   SELECT 'number of rows fetched =', row_count; END; 

The preceding example uses a nested block because an EXIT handler terminates the block within which it is declared, not the loop within which the condition occurs. If no nested block had been used, the handler would transfer control to the end of the main block upon reading the end of the result set, and the CLOSE and SELECT statements following the loop would never execute. An alternative approach does not require a nested block: Use a CONTINUE handler that sets a loop-termination status variable and tests the variable value within the loop. The following example shows one way to do this:

 BEGIN   DECLARE exit_flag INT DEFAULT 0;   DECLARE row_count INT DEFAULT 0;   DECLARE code_var CHAR(3);   DECLARE name_var CHAR(52);   DECLARE c CURSOR FOR     SELECT Code, Name FROM Country WHERE Continent = 'Africa';   DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET exit_flag = 1;   OPEN c;   fetch_loop: LOOP     FETCH c INTO code_var, name_var;     IF exit_flag THEN LEAVE fetch_loop; END IF;     SET row_count = row_count + 1;   END LOOP;   CLOSE c;   SELECT 'number of rows fetched =', row_count; END; 

18.5.7. Retrieving Multiple Result Sets

In standard SQL, a stored procedure that uses SELECT statements to retrieve records processes those records itself (for example, by using a cursor and a row-fetching loop). A MySQL extension to procedures is that SELECT statements can be executed to generate result sets that are returned directly to the client with no intermediate processing. The client retrieves the results as though it had executed the SELECT statements itself. This extension does not apply to stored functions.

An example is the world_record_count() procedure defined in Section 18.4, "Defining Stored Routines." When invoked, it returns three single-record result sets:

 mysql> CALL world_record_count(); +---------+----------+ | Country | COUNT(*) | +---------+----------+ | Country |      239 | +---------+----------+ 1 row in set (0.00 sec) +------+----------+ | City | COUNT(*) | +------+----------+ | City |     4079 | +------+----------+ 1 row in set (0.00 sec) +-----------------+----------+ | CountryLanguage | COUNT(*) | +-----------------+----------+ | CountryLanguage |      984 | +-----------------+----------+ 1 row in set (0.04 sec) Query OK, 0 rows affected (0.04 sec) 

18.5.8. Flow Control

Compound statement syntax includes statements that allow for conditional testing and for creating looping structures:

  • IF and CASE perform conditional testing.

  • LOOP, REPEAT, and WHILE create loops. LOOP iterates unconditionally, whereas REPEAT and WHILE include a clause that tests whether the loop should continue or terminate.

The following sections describe these statements.

18.5.8.1 Conditional Testing

The IF and CASE statements enable you to perform conditional testing. Note that these statements have different syntax than the IF() function and CASE expression. The latter produce a value and are used in expressions. They are not statements in themselves. Also, they end with END rather than END CASE. Because of the differing syntax, it is possible to use IF() functions and CASE expressions within stored routines without ambiguity, even if they occur within IF or CASE statements.

The IF statement tests a condition and then executes other statements depending on whether the condition is true. It has the following syntax:

 IF expr   THEN statement_list   [ELSEIF expr THEN statement_list] ...   [ELSE statement_list] END IF 

The initial conditional expression is evaluated and, if true, the statement list following THEN is executed. To test additional conditions if the initial expression is not true, include one or more ELSEIF clauses. The expression for each is tested in turn until one is found that is true, and then its statement list is executed. The ELSE clause, if present, contains statements to be executed if none of the tested conditions are true.

The following IF statement performs a simple NULL value test:

 IF val IS NULL   THEN SELECT 'val is NULL';   ELSE SELECT 'val is not NULL'; END IF; 

CASE is the other statement that performs conditional testing. It has two forms. The first syntax looks like this:

 CASE case_expr   WHEN when_expr THEN statement_list   [WHEN when_expr THEN statement_list] ...   [ELSE statement_list] END CASE 

The expression case_expr is evaluated and used to determine which of the following clauses in the rest of the statement to execute. The when_expr in the initial WHEN clause is evaluated and compared to case_expr. If the two are equal, the statement list following THEN is exe-cuted. If when_expr is not equal to case_expr, and there are any following WHEN clauses, they are handled similarly in turn. If no WHEN clause has a when_expr equal to case_expr, and there is an ELSE clause, the ELSE clause's statement list is executed.

Each comparison is of the form case_expr = when_expr. The significance of this is that the comparison is never true if either operand is NULL, no matter the value of the other operand.

The following CASE statement tests whether a given value is 0, 1, or something else:

 CASE val   WHEN 0 THEN SELECT 'val is 0';   WHEN 1 THEN SELECT 'val is 1';   ELSE SELECT 'val is not 0 or 1'; END CASE; 

The second CASE syntax looks like this:

 CASE   WHEN when_expr THEN statement_list   [WHEN when_expr THEN statement_list] ...   [ELSE statement_list] END CASE 

For this syntax, the conditional expression in each WHEN clause is executed until one is found to be true, and then its statement list is executed. If none of them are true and there is an ELSE clause, the ELSE clause's statement list is executed. This syntax is preferable to the first syntax for certain types of tests such as those that use IS NULL or IS NOT NULL to test for NULL values, or those that use relative value tests such as val < 0 or val >= 100.

The following CASE statement tests whether a given value is NULL or less than, greater than, or equal to 0:

 CASE   WHEN val IS NULL THEN SELECT 'val is NULL';   WHEN val < 0 THEN SELECT 'val is less than 0';   WHEN val > 0 THEN SELECT 'val is greater than 0';   ELSE SELECT 'val is 0'; END CASE; 

18.5.8.2 Loop Construction

Compound statement syntax in MySQL provides for three kinds of loops:

  • LOOP constructs an unconditional loop with no loop-termination syntax. For this reason, it must contain a statement that explicitly exits the loop.

  • REPEAT and WHILE, the other two loop constructs, are conditional. They include a clause that determines whether loop execution continues or terminates.

Standard SQL includes a FOR loop as well. MySQL does not currently support FOR loops.

Each of the supported loop-construction statements can be labeled. The rules for labeling loops are the same as for labeling BEGIN/END blocks. (See Section 18.5.1, "Compound Statements.")

The LOOP statement that creates an unconditional loop has this syntax:

 LOOP   statement_list END LOOP 

The statement list within the loop executes repeatedly. The loop will, in fact, iterate forever unless the statement list contains some statement that exits the loop. An exit can be effected with a LEAVE statement or (in a function) a RETURN statement. The following LOOP iterates as long as the variable i is less than 10:

 DECLARE i INT DEFAULT 0; my_loop: LOOP   SET i = i + 1;   IF i >= 10 THEN     LEAVE my_loop;   END IF; END LOOP my_loop; 

The REPEAT statement creates a conditional loop. It has this syntax:

 REPEAT   statement_list UNTIL expr END REPEAT 

The statements within the loop execute and then the conditional expression expr is evaluated. If the expression is true, the loop terminates. Otherwise, it begins again. Note that there is no semicolon between the expression and END REPEAT. The following REPEAT loop iterates as long as the variable i is less than 10:

 DECLARE i INT DEFAULT 0; REPEAT   SET i = i + 1; UNTIL i >= 10 END REPEAT; 

The WHILE statement creates a conditional loop. It is similar to REPEAT except that the conditional expression appears at the beginning of the loop rather than at the end. Also, a WHILE loop continues as long as the condition is true, whereas a REPEAT loop terminates as soon as the condition becomes true. WHILE syntax is as follows:

 WHILE expr DO   statement_list END WHILE 

The conditional expression is evaluated and the loop terminates if the condition is not true. Otherwise, the statement list within the loop executes, control transfers back to the beginning, and the expression is tested again. The following WHILE loop iterates as long as the variable i is less than 10:

 DECLARE i INT DEFAULT 0; WHILE i < 10 DO   SET i = i + 1; END WHILE; 

Because the test in a REPEAT is at the end of the loop, the statements within the loop always execute at least once. With WHILE, the test is at the beginning, so it is possible for the statements within the loop not to execute even once. For example, the following WHILE loop never executes the SET statement within the loop:

 WHILE 1 = 0 DO   SET x = 1; END WHILE; 

With any loop construct, be sure that the loop termination condition eventually will become true. Otherwise, the loop will loop forever.

Just as BEGIN/END blocks can be nested, loops can be nested. In such cases, loop labels are useful if it's necessary to exit more than one level of loop at once.

18.5.8.3 Transfer of Control

Two statements provide transfer of control within a routine. Each statement requires a label that indicates which labeled construct it applies to:

 LEAVE label ITERATE label 

LEAVE TRansfers control to the end of the named construct and can be used with blocks and loops: BEGIN/END, LOOP, REPEAT, or WHILE.

ITERATE TRansfers control to the beginning of the named construct. It can be used only within loops: LOOP, REPEAT, or WHILE. It cannot be used to restart a BEGIN/END block.

LEAVE and ITERATE must appear within the labeled construct.

The following example includes a labeled loop and shows how to exit the loop or begin it again with LEAVE and ITERATE.

 DECLARE i INT DEFAULT 0; my_loop: LOOP   SET i = i + 1;   IF i < 10 THEN ITERATE my_loop;   ELSEIF i > 20 THEN LEAVE my_loop;   END IF;   SELECT 'i is between 10 and 20'; END LOOP my_loop; 

The final way to transfer control is by executing a RETURN statement to return a value to the caller. This applies only to stored functions, not stored procedures. The following example returns the country name associated with a country code:

 CREATE FUNCTION countryname(code_param CHAR(3)) RETURNS CHAR(52) BEGIN   DECLARE name_var CHAR(52);   SELECT Name INTO name_var FROM Country WHERE Code=code_param;   RETURN name_var; END; 



MySQL 5 Certification Study Guide
MySQL 5.0 Certification Study Guide
ISBN: 0672328127
EAN: 2147483647
Year: 2006
Pages: 312

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net