#1305 - FUNCTION does not exist - nested loops - mysql

So, I'm getting an error about a function not being defined. It happens every time I try to use my counter variables to refer to specific entries in tables. I don't get it.
To be more clear, I was advised that in order to use loops with mysql I had to make a 'procedure' which I have done. the count and ingredientcount variables are references to the row being examined in the tables tDrinks and tUniqueingredients.
I am trying to generate a foreign key reference for the drink id from tDrinks in the table tDrinkMix. I want there to be an entry of the drink id for each instance of a unique ingredient in the drink. There are 16.5k drinks and 2.2k unique ingredients.
Right now it dies on SELECT id(count) FROM tDrinks. If I remove the (count) there it dies next on WHERE d_shopping(count).
The error thrown is #1305 and it says that the function DrinksDB.id is not defined
DROP PROCEDURE `test`//
CREATE DEFINER=`root`#`localhost` PROCEDURE `test`()
BEGIN
DECLARE count INT DEFAULT 0;
DECLARE ingredientcount INT DEFAULT 0;
WHILE count < 16532 DO
WHILE ingredientcount < 2202 DO
INSERT INTO tDrinkMix(count)
SELECT id(count) FROM tDrinks
WHERE d_shopping(count)
LIKE CONCAT('%',tUniqueingredients.ingredient(ingredientcount),'%');
SET ingredientcount = ingredientcount + 1;
END WHILE;
SET count = count + 1;
END WHILE;
END
So I'm working on refining this a bit, and I'm still not quite there. How can you tell this is my first database project? The following is getting closer I think: the procedure at least saves and looks like it might execute
delimiter //
CREATE DEFINER=`root`#`localhost` PROCEDURE `test`()
BEGIN
DECLARE count INT DEFAULT 0;
DECLARE ingredientcount INT DEFAULT 0;
WHILE count < 16532 DO
WHILE ingredientcount < 2202 DO
INSERT INTO tDrinkMix(drink_id)
SELECT id
FROM tDrinks
WHERE id = count
and
d_shopping
LIKE
(SELECT CONCAT (ingredient,'%') FROM tUniqueingredients WHERE id = ingredientcount);
SET ingredientcount = ingredientcount + 1;
END WHILE;
SET count = count + 1;
END WHILE;
END//

I believe the error is occurring because the parser is interpreting id(count) as a function. It's looking for a function in your database which I guess is DrinksDB. When you remove the (count) from id its moving to the next syntax error of d_shopping(count) and looks for a function with the name d_shopping in your database.
I appreciate I'm late in coming to this (I'm just scanning open issues) so it's probably not an issue any more. If its still a problem post a comment.

Related

Want to generate unique Id's using a function in mysql

I wrote a function to generate unique id's,its working but sometimes two people are getting same id,I mean duplicates are formed. My unique id looks like
2016-17NLR250001, I deal with only last four digits 0001. I am posting my function please correct it and please help me in avoiding duplicates even though users login into same account or if they do it on same time.
MY FUNCTION:
DELIMITER $$
USE `olmsap`$$
DROP FUNCTION IF EXISTS `fun_generate_uniqueid`$$
CREATE DEFINER=`root`#`%` FUNCTION `fun_generate_uniqueid`( V_DATE DATE,V_MANDALID INT ) RETURNS VARCHAR(30) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE MDLCODE VARCHAR(5);
SET MDLCODE = ' ';
SELECT COUNT(*) INTO #CNT FROM `st_com_mandal` WHERE MANDAL_VS_MC=V_MANDALID;
SELECT dist_mandal_code INTO MDLCODE FROM `st_com_mandal` WHERE MANDAL_VS_MC=V_MANDALID;
IF #CNT>0 THEN
SET #YR=`FUN_FISCAL_YR`(V_DATE);
SELECT CONCAT(IF(DIST_SAN_CODE='GUN','GNT',DIST_SAN_CODE),IFNULL(`dist_mandal_code`,'NULL'))INTO #MANDAL
FROM `st_com_dist` SCD INNER JOIN `st_com_mandal` STM ON STM.`mandal_dist_id`= SCD.`DIST_VC_DC` WHERE MANDAL_VS_MC=V_MANDALID;
IF MDLCODE >0 THEN
SELECT COUNT(Soil_Sample_ID)+1 INTO #ID FROM `tt_mao_soil_sample_dtls` WHERE MANDAL_ID=V_MANDALID AND SUBSTR(UNIQUE_ID,1,7)=#YR ;
ELSE
SELECT COUNT(Soil_Sample_ID)+1 INTO #ID FROM `tt_mao_soil_sample_dtls` WHERE SUBSTR(UNIQUE_ID,1,14)=CONCAT(#YR,#MANDAL) ;
END IF ;
IF LENGTH(#ID)=1 THEN
SET #ID=CONCAT('000',#ID);
ELSEIF LENGTH(#ID)=2 THEN
SET #ID=CONCAT('00',#ID);
ELSEIF LENGTH(#ID)=3 THEN
SET #ID=CONCAT('0',#ID);
ELSE
SET #ID=#ID;
END IF ;
RETURN CONCAT(#YR,#MANDAL,#ID);
ELSE
RETURN 'Mandal Doesnt Exists';
END IF;
END$$
DELIMITER ;
I do not think community will be able to help you with this question. This is a complex function that requires very careful analysis of table / index access and locking.
The only thing I can recommend is to not use existing table data to calculate next sequence as this is a bad practice.
Besides Race conditions that you are experiencing you will also get problems if the record with the last sequence is deleted.
I suggest you read this to get an idea on how to write a custom sequence generator:
http://en.latindevelopers.com/ivancp/2012/custom-auto-increment-values/

Updating a column name of a same table which has a parent child relationship using mysql

I searched a lot of doing a task but found no appropriate solution.
Basically the scenario is. I have a user_comment table in which there are 5 column(id,parent_id,user_comments,is_deleted,modified_datetime). There is a parent child relationship like 1->2,1->3,2->4,2->5,5->7 etc. Now i am sending the id from the front end and i want to update the column is_deleted to 1 and modified_datetime on all the records on
this id as well as the all the children and children's of children.
I am trying to doing this by using a recursive procedure. Below is the code of my procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_id INT DEFAULT NULL ;
if(mode = 'delete')
then
update user_comment set is_deleted = 1, modified_datetime = now()
where id = comment_id ;
select id from user_comment where parent_id = comment_id into p_id ;
if p_id is not null
then
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_id);
end if;
end if;
END
By using this procedure it give me an error of more than one row.
If i return the select query without giving it to variable then shows me the the appropriate results on the select query but i have to call this procedure recursively based on getting the ids of the select query.
I need help i have already passed 2 days into this.
I used cursor also. Below is the code of cursor
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_emp int;
DECLARE noMoreRow INT;
DECLARE cur_emp CURSOR FOR select id from user_comment where parent_id = comment_id ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET noMoreRow = 0;
if(mode = 'delete')
then
OPEN cur_emp;
LOOPROWS: LOOP
IF noMoreRow = 0 THEN
update user_comment set is_deleted = 1, modified_datetime = now() where id = comment_id
CLOSE cur_emp;
LEAVE LOOPROWS;
END IF;
FETCH cur_emp INTO p_emp;
update user_comment set is_deleted = 1, modified_datetime = now() where id = p_emp ;
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_emp);
END LOOP;
end if;
END
After using cursor i am getting a thread error.i don't know how can overcome this problem!!!
Mysql's documentation on select ... into varlist clearly says:
The selected values are assigned to the variables. The number of
variables must match the number of columns. The query should return a
single row. If the query returns no rows, a warning with error code
1329 occurs (No data), and the variable values remain unchanged. If
the query returns multiple rows, error 1172 occurs (Result consisted
of more than one row). If it is possible that the statement may
retrieve multiple rows, you can use LIMIT 1 to limit the result set to
a single row.
Since you wrote in the OP that a comment can be parent of many comments, using simple variables cannot be a solution. You should use a CURSOR instead, that can store an entire resultset.
You loop through the records within the cursos as shown in the sample code in the above link and call user_comments() in a recursive way.
UPDATE
If your receive
Error Code: 1436. Thread stack overrun
error, then you can do 2 things:
Increase the thread_stack setting in the config file and restart mysql server.
You can try to simplify your code to use less recursions and therefore less stack space. For example, when you fetch all children into the cursor, then rather calling the user_comments() recursively for each, you can set all direct children's status within the code and call the function recirsively on grand-childrens only (if any). You can also change your data structure and use nested set model to approach hierarchical structures.
Nested set model is more complex to understand, it is less resource intensive to traverse, but more resource intensive to maintain.

I can't execute my stored procedure, my query just keep running

Im just trying using stored procedure in mysql. i tried to create a table name "Book". i have 5 columns before. they are idBook, bookName, bookYear, and Stock. So, i added 2 columns for rating and review. and i wanted to give those column with value using looping in stored procedure. and this is my code :
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `getBookExcitement`()
BEGIN
DECLARE bookRating INT DEFAULT 0;
DECLARE count INT DEFAULT 0;
DECLARE bookReview VARCHAR (100);
DECLARE sums INT;
SELECT count(idBook) INTO sums FROM Book;
getBookExcitement : LOOP
SET count = count+1;
IF count<sums then
SELECT FLOOR(1 + (rand()*5)) INTO bookRating;
IF bookRating <3 then
SET bookReview = 'This book isn't good';
ELSEIF bookRating <=4 OR bookRating >=3 then
SET bookReview = 'This book is good';
ELSE
SET bookReview = 'This book is very good, you should read it';
END IF;
UPDATE Book SET Rating = bookRating AND Review = bookReview
WHERE idBuku=count;
END IF;
END LOOP getBookExcitement;
END
but when i try to call it, my workbench just keep executing the query non stop. Just like it stuck in loop. but the way i see it, i cant find something wrong with that looping, please help me since im new with stored procedure in mysql.
thanks
There's no LEAVE getBookExcitement;
Put an else at the end of the IF count<sums then block, with a LEAVE statement.

How to set a variable as query result in a MySQL stored procudure?

A rather simple question, but I can only find answers to more complex questions.
I'm working on a stored procudure and am currently inside a REPEAT loop. I need to run the following query in the loop to get the 1 column value that is returned (only 1 record will be returned). This will need to be stored as a var to be used later in the loop.
SELECT photo_id FROM photos ORDER BY photo_id DESC LIMIT 1;
How do I set that to 'last_photo_id' to be used later in the stored procdure?
You could do something like this:
SELECT #varname := photo_id
FROM photos
ORDER BY photo_id DESC
LIMIT 1;
That is, if you are sure there's no other way to do what you want to do than in a loop. For SQL works best when you use it for set-based solutions.
Try this:
DECLARE total_products INT DEFAULT 0
SELECT COUNT INTO total_products
FROM products
You can use a function
So for example
SET i_camera_count = get_camera_count(i_photo_camera_data_id);
and then make a function like this.
DELIMITER $$
DROP FUNCTION IF EXISTS `get_camera_count` $$
CREATE DEFINER=`root`#`localhost` FUNCTION `get_camera_count`(camera_id_2 INT(10)) RETURNS int(11)
BEGIN
DECLARE v_return_val INT;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_return_val = -1;
SELECT x FROM y WHERE camera_id = camera_id_2
RETURN v_return_val;
END $$
DELIMITER ;

How to insert multiple rows based on a quantity value in one row?

In MySQL, I am converting a table from a single row per item type (a quantity of items) to a single row per item, so that additional detail can be stored about individual items.
Here is an example source table:
id parent_id qty item_type
-- --------- --- ---------
1 10291 2 widget
2 10292 4 thinger
I want to create a new table with a new column containing info that cannot be applied to more than one item. Thus, the above table would end up as follows:
id parent_id item_type info
-- --------- --------- ----
1 10291 widget [NULL]
2 10291 widget [NULL]
3 10292 thinger [NULL]
4 10292 thinger [NULL]
5 10292 thinger [NULL]
6 10292 thinger [NULL]
Is there a way I can iterate or loop each row of the source table, inserting a number of records equal to the source qty column? I would prefer to do this in sql instead of code to keep all of the conversion steps together (there are many others).
You can do with stored procedure. That will be like below. Below is stored procedure I am using for inserting products into log based on their quantity.
Seem you have to do similar task. You can get how to use database cursor in stored procedure to loop over a result set in MySQL from below example.
DELIMITER $$
DROP PROCEDURE IF EXISTS CursorProc$$
CREATE PROCEDURE CursorProc()
BEGIN
DECLARE no_more_products, quantity_in_stock INT DEFAULT 0;
DECLARE prd_code VARCHAR(255);
DECLARE cur_product CURSOR FOR
SELECT productCode FROM products;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_products = 1;
/* for loggging information */
CREATE TABLE infologs (
Id int(11) NOT NULL AUTO_INCREMENT,
Msg varchar(255) NOT NULL,
PRIMARY KEY (Id)
);
OPEN cur_product;
FETCH cur_product INTO prd_code;
REPEAT
SELECT quantityInStock INTO quantity_in_stock
FROM products
WHERE productCode = prd_code;
IF quantity_in_stock < 100 THEN
INSERT INTO infologs(msg)
VALUES (prd_code);
END IF;
FETCH cur_product INTO prd_code;
UNTIL no_more_products = 1
END REPEAT;
CLOSE cur_product;
SELECT * FROM infologs;
DROP TABLE infologs;
END$$
DELIMITER;
Seems your task is 90% same as above procedure. Just do needful changes. It will work.
I think you can create stored procedure, declare a cursor that reads source table and for each row inserts qty rows into destination table.
Based on other answers which provided some insight, I was able to find additional information (by Kevin Bedell) to create a stored procedure and use a cursor in a loop. I have simplified my solution so that it matches the example in my question:
DROP PROCEDURE IF EXISTS proc_item_import;
DELIMITER $$
CREATE PROCEDURE proc_item_import()
BEGIN
# Declare variables to read records from the cursor
DECLARE parent_id_val INT(10) UNSIGNED;
DECLARE item_type_val INT(10) UNSIGNED;
DECLARE quantity_val INT(3);
# Declare variables for cursor and loop control
DECLARE no_more_rows BOOLEAN;
DECLARE item_qty INT DEFAULT 0;
# Declare the cursor
DECLARE item_cur CURSOR FOR
SELECT
i.parent_id, i.qty, i.item_type
FROM items i;
# Declare handlers for exceptions
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_rows = TRUE;
# Open the cursor and loop through results
OPEN item_cur;
input_loop: LOOP
FETCH item_cur
INTO parent_id_val, item_type_val, quantity_val;
# Break out of the loop if there were no records or all have been processed
IF no_more_rows THEN
CLOSE item_cur;
LEAVE input_loop;
END IF;
SET item_qty = 0;
qty_loop: LOOP
INSERT INTO items_new
(parent_id, item_type)
SELECT
parent_id_val, item_type_val;
SET item_qty = item_qty + 1;
IF item_qty >= quantity_val THEN
LEAVE qty_loop;
END IF;
END LOOP qty_loop;
END LOOP input_loop;
END$$
DELIMITER ;
Before asking this question, I had not used a stored procedures, cursors, or loops. That said, I have read and encountered them frequently on SE and elsewhere, and this was a good opportunity to learn
It may be worth noting that the example on Kevin's page (linked above) does not use END%% (just END) which caused some headache in trying to get the script to work. When creating a procedure, it is necessary to change the delimiter temporarily so that semicolons terminate statements inside the procedure, but not the creation process of the procedure itself.
That is just an example of code that I have here, it is not adapted to your needs, but it does exactly what you need, and it is simple than a procedure, or temporary table.
SELECT event, id, order_ref, storeitem_barcode_create(8), NOW()
FROM (
SELECT mss.id, mss.event, mss.order_ref, mss.quantity, mss.product_id,
#rowID := IF(#lastProductID = mss.product_id AND #lastID = mss.id, #rowID + 1, 0) AS rowID,
#lastProductID := mss.product_id,
#lastID := mss.id
FROM module_barcode_generator mbg,
(SELECT #rowID := 0, #lastProductID := 0, #lastID := 0) t
INNER JOIN module_events_store_sold mss ON mss.order_ref = "L18T2P"
) tbl
WHERE rowId < quantity;
Typo in JYelton's solution for his/her own question:
FETCH item_cur
INTO parent_id_val, item_type_val, quantity_val;
Should be:
FETCH item_cur
INTO parent_id_val, quantity_val, item_type_val;
Otherwise very good.