I have a mysql code that will iterate through the list and changes the total salary field. However, what I don't understand is when dose the value of 'done' change for the loop to stop?. Becaunse UNTIL DONE depends on the value to change. This is an example from a book. Anyway, here is the code:
CREATE PROCEDURE updateSalary() BEGIN
DECLARE done INT DEFAULT 0;
DECLARE current_dnum INT;
DECLARE dnumcur CURSOR FOR SELECT dnumber FROM deptsal;
DECLARE continue HANDLER FOR NOT FOUND SET DONE = 1;
OPEN dnumcur;
REPEAT
FETCH dnumcur INTO current_dnum;
UPDATE deptsal SET totalSalary = (SELECT SUM(salary) FROM employee
WHERE dno=current_dnum) WHERE dnumber=current_dnum;
UNTIL done
END REPEAT;
CLOSE dnumcur;
END$$
delimiter ;
Any help would be appreciated!
Thanks.
The value of done will be changed when the cursor does not find any more data in the data set.
This is controlled by the handler in your code:
DECLARE continue HANDLER FOR NOT FOUND SET done = 1;
This is basically saying, when there is no more data in the data set for this cursor, set the value of done to 1.
There is some extra information on this at: https://dev.mysql.com/doc/refman/5.7/en/declare-handler.html - that reads "NOT FOUND: Shorthand for the class of SQLSTATE values that begin with '02'. This is relevant within the context of cursors and is used to control what happens when a cursor reaches the end of a data set. If no more rows are available, a No Data condition occurs with SQLSTATE value '02000'. To detect this condition, you can set up a handler for it or for a NOT FOUND condition."
Hope that helps :)
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 am trying to use a cursor in MySQL to call a stored procedure many times. I want to call it as many times as a value for my_id exists in some temporary table, and iterate through those ids and concatenate the results.
Anyway, I'm having trouble with this part of the process:
DECLARE curs CURSOR FOR
SELECT something FROM somewhere;
I don't want to select something from somewhere. I want something like
DECLARE curs CURSOR FOR
CALL storedproc(#an_id);
Can the DECLARE statement be used to call a stored procedure? Or does it have to be associated with a SELECT only? Googling around, I'm afraid that the latter is the case.
Using a cursor requires some standard boilerplate code to surround it.
Using a cursor to call a stored procedure for each set of values from the table requires essentially the same boilerplate. You SELECT the values you want to pass, from wherever you're getting them (which could be a temporary table, base table, or view, and can include calls to stored functions) and then call the procedure with those values.
I've written an syntactically valid example of that boilerplate code, below, with comments to explain what each component is doing. There are few things I dislike more than being asked to just do something "just because" -- so everything is (hopefully) explained.
You mentioned calling the procedure with multiple values, so this example uses 2.
Note that there events that happen her are in a specific order for a reason. Variables have to be declared first, cursors have to be declared before their continue handlers, and loops have to follow all of those things. This gives an impression that there's some fairly extreme inflexibility, here, but that's not really the case. You can reset the ordering by nesting additional code inside BEGIN ... END blocks within the procedure body; for example, if you needed a second cursor inside the loop, you'd just declare it inside the loop, inside another BEGIN ... END.
DELIMITER $$
DROP PROCEDURE IF EXISTS `my_proc` $$
CREATE PROCEDURE `my_proc`(arg1 INT) -- 1 input argument; you might not need one
BEGIN
-- from http://stackoverflow.com/questions/35858541/call-a-stored-procedure-from-the-declare-statement-when-using-cursors-in-mysql
-- declare the program variables where we'll hold the values we're sending into the procedure;
-- declare as many of them as there are input arguments to the second procedure,
-- with appropriate data types.
DECLARE val1 INT DEFAULT NULL;
DECLARE val2 INT DEFAULT NULL;
-- we need a boolean variable to tell us when the cursor is out of data
DECLARE done TINYINT DEFAULT FALSE;
-- declare a cursor to select the desired columns from the desired source table1
-- the input argument (which you might or might not need) is used in this example for row selection
DECLARE cursor1 -- cursor1 is an arbitrary label, an identifier for the cursor
CURSOR FOR
SELECT t1.c1,
t1.c2
FROM table1 t1
WHERE c3 = arg1;
-- this fancy spacing is of course not required; all of this could go on the same line.
-- a cursor that runs out of data throws an exception; we need to catch this.
-- when the NOT FOUND condition fires, "done" -- which defaults to FALSE -- will be set to true,
-- and since this is a CONTINUE handler, execution continues with the next statement.
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- open the cursor
OPEN cursor1;
my_loop: -- loops have to have an arbitrary label; it's used to leave the loop
LOOP
-- read the values from the next row that is available in the cursor
FETCH NEXT FROM cursor1 INTO val1, val2;
IF done THEN -- this will be true when we are out of rows to read, so we go to the statement after END LOOP.
LEAVE my_loop;
ELSE -- val1 and val2 will be the next values from c1 and c2 in table t1,
-- so now we call the procedure with them for this "row"
CALL the_other_procedure(val1,val2);
-- maybe do more stuff here
END IF;
END LOOP;
-- execution continues here when LEAVE my_loop is encountered;
-- you might have more things you want to do here
END $$
DELIMITER ;
Can the DECLARE statement be used to call a stored proc?
Not possible and documentation is pretty clear on that
Cursor DECLARE Syntax
This statement declares a cursor and associates it with a SELECT statement that retrieves the rows to be traversed by the cursor. To fetch the rows later, use a FETCH statement. The number of columns retrieved by the SELECT statement must match the number of output variables specified in the FETCH statement.
For example , is it ok to do below ?
DECLARE aId VARCHAR(20);
DECLARE cur1 CURSOR FOR SELECT id FROM new_records WHERE is_loaded = false;
read_loop: LOOP
FETCH cur1 INTO aId;
...
update new_records set is_loaded = True where id = aId ;
...
CLOSE cur1;
END
Cursors in MySQL are ASENSITIVE (13.6.6 Cursors).
An INSENSITIVE Cursor is a Cursor that effectively causes a separate
copy of its result Table to be created; the Cursor accesses that copy,
rather than the original result, so any changes made to the original
result by other methods won't be visible to this Cursor. A SENSITIVE
Cursor is a Cursor that works directly on its result Table: it makes
no copy, so other changes made to the result Table will be visible to
this Cursor. An ASENSITIVE Cursor may or may not make a copy of its
result Table; whether other changes to its result Table will be
visible is implementation-defined. The default is an ASENSITIVE
Cursor.
- From DECLARE CURSOR Statement -
SQL Fiddle demo
However, depending on what you need to do, there are other ways to update the table.
I have changed this stored procedure many times and still keep getting the same error. I know I am getting rows back because I tested the cursor select separately. I have read other similar issues on this site but I don't seem to have the same fix.
DECLARE iDone INTEGER(10) DEFAULT 0;
DECLARE userID INTEGER(10);
DECLARE creditRemaining INTEGER(10);
DECLARE column_cur CURSOR FOR
SELECT `userID`, `creditRemaining` FROM `access`
WHERE (`dateExpire`>=now() OR `isRenewed`=1) and `descriptionShort`='Subscription';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET iDone=1;
SET userID = 0;
SET creditRemaining = 0;
OPEN column_cur;
LOOP1: LOOP
FETCH column_cur INTO userID, creditRemaining;
IF iDone = 1
THEN
LEAVE LOOP1;
END IF;
-- SELECT userID, creditRemaining;
UPDATE `users` SET `saveCount`=creditRemaining, `searchCount`=6000 WHERE `ID`=userID;
END LOOP LOOP1;
CLOSE column_cur;
I think this is a duplicate of more, except for the fact you already have a HANDLER declared. In another Error 1329 question, this answer from #DanJGer pointed out a note in the docs:
Before MySQL 5.6.3, if a statement that generates a warning or error causes a condition handler to be invoked, the handler may not clear the diagnostic area. This might lead to the appearance that the handler was not invoked. The following discussion demonstrates the issue and provides a workaround.
That's likely the problem you're encountering.
I've read this:
MySQL Fulltext search against column value?
but it is absurd, i don't understand why it is not possible doing this.
anyone knowing some workaround?
thanks
This is my old procedure, try something like this in your case too-
BEGIN
declare done default 0;
declare csv1 varchar(100);
declare cur1 cursor for select csv from table;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
open cur1;
repeat
fetch cur1 into csv1;
-
-
-
-
update company set (something) where match(csv) against(concat("'",csv1,"'"));
until done end repeat;
-
-
close cur1;
select * from table;
END
Because the data is stored on the catalog. When you query a full text index, you are reading data from the catalog it is created, not the table the index is pointing to.
When you "populate" the catalog, you are telling your server to read data from the table and insert on the catalog. If you have a non static value, how will you read it?