Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic cursor in stored procedure

I would like to use LIMIT in a cursor. The cursor should be used and updated several times within a loop, each time with different parameters of LIMIT. Here some code:

DELIMITER $$
CREATE PROCEDURE `updateIt`() READS SQL DATA
BEGIN

declare done int(1) default 0;
declare counter int(10) default 0;
declare xabc int(10) default 0;

declare tab1Cursor cursor for select abc from tab1 limit 100000*counter, 100000;
declare continue handler for not found set done=1;

loopCounter: LOOP
    set done = 0;
    open tab1Cursor;
    igmLoop: loop
        fetch tab1Cursor into xabc;
        if done = 1 then leave igmLoop; end if;
        -- do something
    end loop igmLoop;
    close tab1Cursor;

    if (counter = 1039) 
        leave loopCounter;
    end if;
    set counter = counter + 1;

END LOOP loopCounter;
END $$
DELIMITER ;

This, however, does not work (I also tried it with the cursor in the LOOP counterLoop). Can Mysql deal with dynamic cursors?

like image 904
Marcus Avatar asked Oct 07 '11 09:10

Marcus


People also ask

What is a dynamic cursor?

Dynamic cursors detect all changes made to the rows in the result set, regardless of whether the changes occur from inside the cursor or by other users outside the cursor. All insert, update, and delete statements made by all users are visible through the cursor.

Can we use cursor in stored procedure?

Cursors are particularly useful in stored procedures. They allow you to use only one query to accomplish a task that would otherwise require several queries. However, all cursor operations must execute within a single procedure.

Can we use dynamic SQL in cursor?

A cursor will only accept a select statement, so if the SQL really needs to be dynamic make the declare cursor part of the statement you are executing. For the below to work your server will have to be using global cursors.

Can we use dynamic SQL in stored procedure?

You can use only a DDL COMMENT statement as dynamic SQL in a stored procedure. You cannot specify a DML COMMENT statement to fetch the comments for database objects, columns of a table, and parameters. A CREATE DATABASE or CREATE USER statement used as dynamic SQL in a stored procedure must contain the FROM clause.


1 Answers

From the MySQL Manual

a cursor cannot be used for a dynamic statement that is prepared and executed with PREPARE and EXECUTE. The statement for a cursor is checked at cursor creation time, so the statement cannot be dynamic.

However there are 2 ways, according to this post in mysql forums:

The first is for cases where absolutely only one user at a time will be running the procedure. A prepare statement can be used to create a view with the dynamic SQL and the cursor can select from this statically-named view. There's almost no performance impact. Unfortunately, these views are also visible to other users (there's no such thing as a temporary view), so this won't work for multiple users.

Analogously, a temporary table can be created in the prepare statement and the cursor can select from the temporary table. Only the current session can see a temporary table, so the multiple user issue is resolved. But this solution can have significant performance impact since a temp table has to be created each time the proc runs.

Bottom line: We still need cursors to be able to be created dynamically!

Here's an example of using a view to pass the table name and column name into a cursor.

DELIMITER // 
DROP PROCEDURE IF EXISTS test_prepare// 

CREATE PROCEDURE test_prepare(IN tablename varchar(255), columnname varchar(50)) 
BEGIN 
DECLARE cursor_end CONDITION FOR SQLSTATE '02000'; 
DECLARE v_column_val VARCHAR(50); 
DECLARE done INT DEFAULT 0; 
DECLARE cur_table CURSOR FOR SELECT * FROM test_prepare_vw; 
DECLARE CONTINUE HANDLER FOR cursor_end SET done = 1; 

SET @query = CONCAT('CREATE VIEW test_prepare_vw as select ', columnname, ' from ', tablename); 
select @query; 
PREPARE stmt from @query; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

OPEN cur_table; 
FETCH cur_table INTO v_column_val; 
WHILE done = 0 DO 
SELECT v_column_val; 
FETCH cur_table INTO v_column_val; 
END WHILE; 
CLOSE cur_table; 

DROP VIEW test_prepare_vw; 

END; 
// 

DELIMITER ;
like image 123
Pentium10 Avatar answered Sep 20 '22 19:09

Pentium10