You are on page 1of 5

Creating a stored procedure in MySQL: DELIMITER $$ DROP PROCEDURE IF EXISTS HelloWorld$$ CREATE PROCEDURE HelloWorld () BEGIN SELECT 'Hello

World'; END$$ There are no anonymous blocks in MySQL. Define a cursor with the DECLARE statement, which has the following syntax:
DECLARE cursor_name CURSOR FOR SELECT_statement;

A cursor is always associated with a SELECT statement. There are no dynamic cursors in MySQL. The MySQL stored program language supports three statements for performing operations on cursors :
OPEN

Initializes the result set for the cursor. We must open a cursor before fetching any rows from that cursor. The syntax for the OPEN statement is very simple:
OPEN cursor_name; FETCH

Retrieves the next row from the cursor and moves the cursor "pointer" to the following row in the result set. It has the following syntax:
FETCH cursor_name INTO variable list;

The variable list must contain one variable of a compatible data type for each column returned by the SELECT statement contained in the cursor declaration. We'll discuss FETCH in more detail later in this chapter.
CLOSE

Deactivates the cursor and releases the memory associated with that cursor. The syntax for this statement is:
CLOSE cursor_name ;

We should close a cursor when we have finished fetching from it, or when we need to open that cursor again after changing a variable that affects the cursor's result set. Example of cursor in MySQL:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_row_fetched=1; SET l_last_row_fetched=0; OPEN cursor1; cursor_loop:LOOP FETCH cursor1 INTO l_customer_name,l_contact_surname,l_contact_firstname; IF l_last_row_fetched=1 THEN LEAVE cursor_loop; END IF; /*Do something with the row fetched*/ END LOOP cursor_loop; CLOSE cursor1; SET l_last_row_fetched=0;

Explanation:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_row_fetched=1;

This handler instructs MySQL to do two things when the "no data to fetch" scenario occurs: 1. Set the value of the "last row variable" (l_last_row_fetched) to 1. 2. Allow the program to continue executing.

The program can now check the value of l_last_row_fetched. If it is set to 1, then we know that the last row has been fetched, and we can terminate the loop and close the cursor. Nested cursor loops
CREATE PROCEDURE good_nested_cursors1( READS SQL DATA BEGIN DECLARE DECLARE DECLARE DECLARE l_department_id l_employee_id l_emp_count l_done )

INT; INT; INT DEFAULT 0 ; INT DEFAULT 0;

DECLARE dept_csr cursor FOR SELECT department_id FROM departments; DECLARE emp_csr cursor FOR SELECT employee_id FROM employees WHERE department_id=l_department_id; DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_customer=1; SET l_last_customer=0; OPEN customer_csr; cust_loop:LOOP /* Loop through overdue customers*/ FETCH customer_csr INTO l_customer_id; IF l_last_customer=1 THEN LEAVE cust_loop; END IF; /*no more rows*/ SET l_customer_count=l_customer_count+1; sales_block: BEGIN DECLARE l_last_sale INT DEFAULT 0; DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_sale=1; OPEN sales_csr; sales_loop:LOOP /* Get all sales for the customer */ FETCH sales_csr INTO l_sales_id; IF l_last_sale=1 THEN LEAVE sales_loop; END IF; /*no more rows*/ CALL check_sale(l_sales_id); /* Check the sale status */ SET l_sales_count=l_sales_count+1; END LOOP sales_loop; SET l_last_sale=0; CLOSE sales_csr; END sales_block; END LOOP cust_loop; SET l_last_customer=0; CLOSE customer_csr; END;

Alternative for dynamic cursors, taken from: http://mysql-0v34c10ck.blogspot.com/2011/05/dynamic-cursorfix.html As of version 5.5, MySQL still does not have the native ability to perform a dynamic cursor. This can be worked around but the resulting stored procedure will have a few limitations. This stored procedure is a general purpose dynamic cursor. It is general purpose, in the sense that, it qualifies as a template that you can modify to suite a specific purpose and/or expand to cover a broad range of purposes. It can be called by passing the following parameters: CALL dynamicCursor( "SELECT * FROM `dbName`.`tableName`", 'whatAction', @inOutParam); The first parameter is the SELECT statement that the cursor is declared to use. Quotation marks can be used to encapsulate the string so that single quotes can be used by the statement itself if needed. The second parameter is a string that specifies what action to do with the cursor traversal data-- the statements to execute are written inside the stored procedure as cases of the second parameter. What you do with the data is up to you. The third one is an INOUT variable in case a return is needed, or additional parameters need to be passed depending on the requirements. You can expand the number of INOUT variables to the needed amount by simply adding more in the stored procedure. On the other hand, if a return or passing additional variables is not needed as the case may be, simply set the INOUT variable to a dummy variable such as @NULL. The dynamic cursor logic can be achieved through a dynamic MySQL statement that creates a copy of the data from the SELECT statement of the cursor declaration into a created table. The cursor, instead of using the SELECT statement, is declared to instead SELECT the table that was created. This causes the table data to be the part that is dynamic and not the cursor itself in a strict sense. DELIMITER $$ DROP PROCEDURE IF EXISTS `dynamicCursor` $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `dynamicCursor`( IN selectStmt TEXT, IN whatAction VARCHAR(255), INOUT inOutParam VARCHAR(255)) BEGIN #Used by whatAction ='debug' DECLARE cursorRead TEXT DEFAULT ''; #Cursor-specific variables DECLARE colVal1, colVal2, colVal3 TEXT; DECLARE noMoreRows BOOLEAN DEFAULT FALSE; DECLARE dynamicCursor CURSOR FOR SELECT * FROM `dynamic_cursor`; DECLARE CONTINUE HANDLER FOR 1329 SET noMoreRows = TRUE;

DROP TABLE IF EXISTS `dynamic_cursor`; SET @createTableStmt = CONCAT( 'CREATE TABLE `dynamic_cursor` AS ', selectStmt); PREPARE createTableStmt FROM @createTableStmt; EXECUTE createTableStmt; DEALLOCATE PREPARE createTableStmt; CALL dynamicCursorFix('dynamic_cursor'); OPEN dynamicCursor; dynamicCursorLoop: LOOP FETCH dynamicCursor INTO colVal1, colVal2, colVal3; IF (noMoreRows) THEN CLOSE dynamicCursor; LEAVE dynamicCursorLoop; END IF; CASE whatAction WHEN 'debug' THEN SET cursorRead = CONCAT_WS(', ', cursorRead, colVal1, colVal2, colVal3); ELSE BEGIN END; END CASE; END LOOP dynamicCursorLoop; CASE whatAction WHEN 'debug' THEN SELECT cursorRead 'Values read by the cursor'; ELSE BEGIN END; END CASE; DROP TABLE `dynamic_cursor`; END $$ DELIMITER ; This is the workaround to create dynamic cursors in MySQL and is meant to be used together with the general purpose dynamic cursor stored procedure. Increase the compared value of colValN in the WHILE-DO construct to the maximum number of columns your dynamic cursor will be needing. This fixes MySQL's limitation on cursors by providing the dynamic cursor with consistent column names that are independent of the underlying SELECT statement and also provide a predictable number of columns. The final result is that the temporarily created table will be constant, while the records that the cursor will traverse will remain dynamic. The code in part 3 of the general purpose dynamic cursor together with this fix creates a working dynamic cursor that can be called multiple times using different SELECT statement parameters with no stale table data returned.

DELIMITER $$ DROP PROCEDURE IF EXISTS `dynamicCursorFix` $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `dynamicCursorFix`( IN tableName VARCHAR(64)) BEGIN DECLARE columnNo TINYINT DEFAULT 0; DECLARE columnName TEXT; DECLARE noMoreColumns BOOLEAN DEFAULT FALSE; DECLARE columnNameCursor CURSOR FOR SELECT `COLUMN_NAME` FROM `information_schema`.`COLUMNS` WHERE (`TABLE_SCHEMA` = DATABASE()) AND (`TABLE_NAME` = tableName); DECLARE CONTINUE HANDLER FOR 1329 SET noMoreColumns = TRUE; OPEN columnNameCursor; columnNameCursorLoop: LOOP FETCH columnNameCursor INTO columnName; IF (noMoreColumns) THEN CLOSE columnNameCursor; LEAVE columnNameCursorLoop; END IF; SET columnNo = columnNo + 1; SET @changeColStmt = CONCAT( 'ALTER TABLE `', tableName, '` CHANGE `', columnName, '` `column', columnNo, '` TEXT'); PREPARE changeColStmt FROM @changeColStmt; EXECUTE changeColStmt; DEALLOCATE PREPARE changeColStmt; END LOOP columnNameCursorLoop; #Increase as needed by the dynamic cursor WHILE columnNo < 3 DO SET columnNo = columnNo + 1; SET @addColStmt = CONCAT( 'ALTER TABLE `', tableName, '` ADD `column', columnNo, '` TINYINT'); PREPARE addColStmt FROM @addColStmt; EXECUTE addColStmt; DEALLOCATE PREPARE addColStmt; END WHILE; END $$ DELIMITER ;

You might also like