I have this sql procedure:
create procedure DELETE as
DECLARE VARIABLES
begin TRY
DECLARE CURSOR FOR SELECT ANYTHING
OPEN CURUPD
FETCH NEXT FROM CURUPD INTO VARIABLES
WHILE ##FETCH_STATUS = 0 BEGIN
UPDATE TABLE BASED SON VARIABLES
FETCH NEXT FROM CURUPD INTO VARIABLES
END
CLOSE CUR
DEALLOCATE CUR
end TRY
begin catch
DO NOT CARE ABOUT THE ERROR BUT CONTINUE UPDATE ON NEXT RECORD
end catch;
Is this possible?
Robert :-)
You would have to put the TRY/CATCH inside of the WHILE loop. The way you have it, you can't continue the WHILE loop, because it's already finished.
Try this code:
create procedure DELETE as
DECLARE VARIABLES
begin TRY
DECLARE CURSOR FOR SELECT ANYTHING
OPEN CURUPD
FETCH NEXT FROM CURUPD INTO VARIABLES
WHILE ##FETCH_STATUS = 0 BEGIN
UPDATE TABLE BASED SON VARIABLES
FETCH NEXT FROM CURUPD INTO VARIABLES
END
CLOSE CUR
DEALLOCATE CUR
end TRY
begin catch
FETCH NEXT FROM CURUPD INTO VARIABLES
continue
end catch;
some errors are not trappable and will abort the batch even with try and catch you could check XACT_STATE() and then determine what to do
Why do you need a cursor can't you do a SET based update?
Also your try is outside of the loop but you will still have the same problem, try updating an int colum,n with 'A'....BOOM!
Related
I usually work with Oracle database and when creating stored procedures one can write cursors where the where clause can have a variable, value of which can be provided at run time.
How do you write something similar in mySQL
Something like
DECLARE myCursor cursor select col1 from table1 where col2 = &1;
OPEN myCursor ("NEW");
You may use user-defined variable and/or local variable (including procedure parameters), both assigned externally and calculated internally, in the cursor definition:
CREATE PROCEDURE test_proc( {parameters} )
BEGIN
DECLARE _id_ INT;
DECLARE cur CURSOR FOR
SELECT id FROM test WHERE val = {variable};
DECLARE EXIT HANDLER FOR NOT FOUND
BEGIN
CLOSE cur;
END;
OPEN cur;
LOOP
FETCH cur INTO _id_;
SELECT _id_;
END LOOP;
END
But you cannot to alter the parameter "on the fly" - after cursor is opened the changes in its parameters will be iglored (during the opening the cursor's text is fixed, the values instead of names are used in fixed text).
Hence when you need to alter dynamically assigned parameter then you must close and reopen the cursor.
DEMO fiddle
I get this error in the following stored procedure Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; OPEN cur1; count' at line 13
DELIMITER $$
DROP PROCEDURE IF EXISTS cursor_example
$$
CREATE PROCEDURE cursor_example()
READS SQL DATA
BEGIN
DECLARE i_Name CHAR(3);
DECLARE i_SurfaceArea FLOAT(10,2);
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR
SELECT Name, SurfaceArea
FROM country
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN cur1;
country_loop:LOOP
FETCH cur1 INTO i_Name, i_SurfaceArea;
IF done=1 THEN
LEAVE country_loop;
END IF;
END LOOP country_loop;
CLOSE cur1;
END;
$$
DELIMITER ;
Just generally how the whole procedure is suppose to work and what is it for.
Thanks.
You were missing a semi-colon after FROM country. Use the following pattern:
DROP PROCEDURE IF EXISTS cursor_example;
DELIMITER $$
CREATE PROCEDURE cursor_example()
READS SQL DATA
BEGIN
DECLARE i_Name CHAR(3);
DECLARE i_SurfaceArea FLOAT(10,2);
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR
SELECT Name, SurfaceArea
FROM country;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN cur1;
country_loop:LOOP
FETCH cur1 INTO i_Name, i_SurfaceArea;
IF done=1 THEN
LEAVE country_loop;
END IF;
-- right here is where you do stuff with those variables
END LOOP country_loop;
CLOSE cur1;
END;
$$
DELIMITER ;
I don't understand with the example is trying to FETCH cur1 INTO in
the example is for?
Remember that the CURSOR is just a select stmt. It can be really complicated with joins, you name it. But in the end it has a select column list. In your case it has 2 columns coming back. So the FETCH, one row at a time, brings the current row into LOCAL VARIABLES (in the respective order from the cursor list to the variables you list). You declared those LOCAL VARIABLES in your DECLAREs.
When you are out of rows, the HANDLER sets done to 1 and you bail out of the loop.
As for the DELIMITER read the last half of this answer of mine Here.
Just generally how the whole procedure is suppose to work and what is
it for?
Described above mostly. Cursors are for procedural handling of data returned. Allowing you to inject procedural thinking into solving problems. By the way they are terribly slow and should be avoided whenever possible. They are typically a crutch for devs new to SQL that can't get their head into how to do work with sets and relations. That is, the way high performance RDBMS's excel at.
That said, experienced SQL devs are known to use them for tricky situations.
I have built a MySQL procedure.
i have to define v_table variable within procedure
Who can help me ?
Thanks
Paolo
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open cur1;
igmLoop: loop
fetch cur1 into v_column,v_table;
IF done THEN
LEAVE igmLoop;
END IF;
update v_table set v_column=replace(v_column,'Ã ','a`');
end loop igmLoop;
close cur1;
end
Neither table names, nor column names can be dynamic within a stored procedure. You need to assemble the sql statement as a string and execute it using PREPARE, EXECUTE, DEALLICATE PREPARE statements. So, basically, you will create a prepared statement out of your query and execute it.
The linked documentation contains examples as well.
I have a sp with two nested cursors. The outer cursor is customers, and the inner cursor is periods.
when an error occurs in the inner cursor I want to rollback what has been done for the specific customer and proceed with the processing of the next customer. However when the inner cursor is executed for the next customer (after an exception has occurred) I get an "cursor is already open message"
The code looks like this:
DECLARE customers CURSOR FOR
select * from customers_table;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_customers = 1;
OPEN customers;
customers_cursor:
REPEAT
FETCH customers
INTO ....
IF no_more_customers = 1
THEN
close customers;
LEAVE customers_cursor;
END IF;
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
rollback;
END;
....... //do some stuff
BEGIN
DECLARE no_more_periods INT(1) DEFAULT 0;
DECLARE periods CURSOR FOR
SELECT ...
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_periods = 1;
OPEN periods;
periods_cursor:
REPEAT
FETCH periods INTO...
IF no_more_periods = 1
THEN
close periods;
LEAVE periods_cursor;
END IF;
..... //do some stuff point 1
UNTIL no_more_periods = 1
END REPEAT periods_cursor;
end;
END;
UNTIL no_more_customers = 1
END REPEAT customers_cursor;
END;
Customer 1 runs and and exception occurs in //do some stuff point 1. Then customer 2 runs until the open periods statement is reached and that's when I get the "Cursor is already open".
Thanks a lot for your help.
I do not see any transactions or commits in your code. I would change the code to include the following:
Start a new transaction for each customer
Commit this transaction before starting on the next customer.
Close the periods cursor before performing a rollback.
thanks a lot for your help #JodyT .I am initiating a new block for each loop of the customer cursor (begin after the "if no_more_customer=1" block". Also i am doing "commit" before next customer. Unfortunately i cannot close periods cursor in the exception handler of this block (cursor periods not defined in this scope-compile time error).What i did to overcome the issue was to define an exception handler inside the inner loop (exactly after the continue handler of the periods cursor).In this handler i close periods and then raise exception to perform rollback in the outter exception handler
I have got this stored procedure:
DELIMITER //
DROP PROCEDURE IF EXISTS cursor_example//
CREATE PROCEDURE cursor_example()
BEGIN
DECLARE niche_id INT;
DECLARE niche_name VARCHAR(100);
DECLARE curl CURSOR FOR SELECT * FROM `niche`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN curl;
my_loop:LOOP
FETCH curl INTO niche_id,niche_name;
IF done=1 THEN
LEAVE my_loop;
END IF;
END LOOP my_loop;
CLOSE curl;
END//
DELIMITER ;
I want to output everything that the curl fetches. So I thought to put SELECT statement inside or outside the loop..but that wouldnt give me the results that I want. How do I get back all the sql results from that cursor.
And what are the advantages of using cursors compare to other a simple unbound - SELECT statement. I mean I could get the results that I wanted by simply USING a select statement without writing all that cursor code?