How to do unlimited recursion in mysql? - mysql

I have a mysql stored procedure made it recursively, it always returned 499 rows max.
my stored procedure is moving in a tree (not a binary tree) and check the nodes if they have children and so on until it reached the leaves.
I don't know how can I convert my code into non-recursive way, I just want to ask for tow points:
how can I make an infinite recursive in mysql(mysql server version is 5.5)?
if that can't happened, how can I change my cod into non-recursive way?
CREATE PROCEDURE `get_citations`(in _pub_id int(10),in _lvl int,citation_count int)
BEGIN
DECLARE done INT DEFAULT FALSE;
declare p_id,c_count int;
declare _counter int default 1;
DECLARE cur1 CURSOR FOR SELECT pat_publn_id,cited_count from temp.a_citations
where pub_parent=_pub_id ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
insert into a_citations
(pat_publn_id ,
publn_nr ,
publn_kind,
publn_auth,
publn_date,
cited_pat_publn_id,
cited_count,
pub_lvl,
pub_parent)
(select p.pat_publn_id,p.publn_nr,p.publn_kind,p.publn_auth,p.publn_date,c.cited_pat_publn_id,
(select count(*) as cnt FROM patstat1304.tls212_citation c2 where c2.cited_pat_publn_id=c.pat_publn_id) as cited_count,_lvl as pub_lvl,_pub_id as pub_parent
from patstat1304.tls212_citation c,patstat1304.tls211_pat_publn p
where c.pat_publn_id=p.pat_publn_id and c.cited_pat_publn_id=_pub_id);
commit;
OPEN cur1;
read_loop: LOOP
fetch cur1 into p_id,c_count;
IF (c_count !=0) then
call get_citations( p_id,_lvl+1,c_count);
commit;
END if;
IF done THEN
LEAVE read_loop;
END IF;
set _counter=_counter+1;
if(_counter=citation_count) then
LEAVE read_loop;
end if;
end loop;
CLOSE cur1;
END

MySQL can not execute stored procedures with a very deep nesting.
Very soon, error ER_STACK_OVERRUN_NEED_MORE will appear.
Increasing the thread stack to go further will not work either.
To change the recursive call to a non recursive one, consider something like this:
1) Create a table named publications_to_process, with the publication and search level.
2) To start the search, insert the original publication in this table, with level 1.
3) In a loop, fetch one publication, examine the citations, and add the publications listed in the publications_to_process, incrementing the level.
4) As a bonus, for cases like:
Pub_1 --> Pub_2 --> Pub_3,
Pub_1 --> Pub_3
there is no need to add Pub_3 again to the search if it has been processed already.
In other words, the publications are more likely to be a directed graph that a tree.
5) Either make the table temporary, or consider adding a PROCESSLIST_ID column, so that different sessions (having a different CONNECTION_ID()) do not step on each other, when executing this search in parallel.

Related

MySQL - how to use variable in where clause of cursor

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

Error 1064 in stored procedures

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.

Call a stored procedure from the DECLARE statement when using cursors in MySQL

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.

Call a stored procedure for each row returned by a query in MySQL

I want a MySQL stored procedure which effectively does:
foreach id in (SELECT id FROM objects WHERE ... ) CALL testProc(id)
I think I simply want the MySQL answer to this question but I don't understand cursors well: How do I execute a stored procedure once for each row returned by query?
Concepts such as “loops” (for-each, while, etc) and “branching” (if-else, call, etc) are procedural and do not exist in declarative languages like SQL. Usually one can express one’s desired result in a declarative way, which would be the correct way to solve this problem.
For example, if the testProc procedure that is to be called uses the given id as a lookup key into another table, then you could (and should) instead simply JOIN your tables together—for example:
SELECT ...
FROM objects JOIN other USING (id)
WHERE ...
Only in the extremely rare situations where your problem cannot be expressed declaratively should you then resort to solving it procedurally instead. Stored procedures are the only way to execute procedural code in MySQL. So you either need to modify your existing sproc so that it performs its current logic within a loop, or else create a new sproc that calls your existing one from within a loop:
CREATE PROCEDURE foo() BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE _id BIGINT UNSIGNED;
DECLARE cur CURSOR FOR SELECT id FROM objects WHERE ...;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
testLoop: LOOP
FETCH cur INTO _id;
IF done THEN
LEAVE testLoop;
END IF;
CALL testProc(_id);
END LOOP testLoop;
CLOSE cur;
END

Fulltext search against column value?

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?