MySQL query update with subquery multiple values - mysql

I need to update a table based on the result of a subquery that brings more than 1 value, but with the query below I get the error return "Subquery returns more than 1 row". I would like to know if it is possible to make a "loop" to update the values ​​with each result presented in my subquery below.
Complete Query
UPDATE estoque_tamanhos tam
SET tam.qtde = tam.qtde - IF(NEW.tipo = 'K', NEW.qtde_prod * NEW.qtde_lote, NEW.qtde_prod)
WHERE tam.estoques_id = (SELECT estoques_id
FROM combo_estoque
WHERE produtos_id = NEW.produtos_id)
AND UPPER(tam.tamanho) = UPPER(NEW.tamanho_prod);
Subquery that returns 2 or more values.
SELECT estoques_id FROM combo_estoque WHERE produtos_id = NEW.produtos_id
Result
produtos_id
estoques_id
246
54
246
207
In the ideal scenario, my query would execute the first value, after the second ... third ... without repeating the previous ones.

You should create a stored procedure using Cursors to iterate each result of your query.
CREATE PROCEDURE UPDATING_ROWS(PRODUTO_ID INT, qtde_prod INT, qtde_lote INT, TIPO INT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE estoque_;
DECLARE cursor FOR SELECT estoques_id FROM combo_estoque WHERE produtos_id = NEW.produtos_id) AND UPPER(tam.tamanho) = UPPER(NEW.tamanho_prod)
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO estoque_;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE estoque_tamanhos tam SET tam.qtde = tam.qtde - IF(TIPO = 'K',
qtde_prod * qtde_lote, qtde_prod)
WHERE tam.estoques_id = estoque_;
END LOOP;
END
You have to create a stored procedure like this to iterate each result of your select query. You can read more about this in this link

Related

Differentiate between no results returned and end of rows for cursor

I'm trying to implement a cursor in MYSQL. Inside that cursor, I have a select statement that might or might not return any rows.
I'm facing a problem when that query does not return any rows.
According to the MYSQL documentation,
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'. You can set up a handler for it or a NOT-FOUND condition to detect this condition. For another example, see Section 13.6.6, “Cursors”. The NOT FOUND condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
My termination of the cursor is implemented as usual.
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
The problem is that the termination is triggered because of the query inside the cursor returning 0 rows. Is there a way to differentiate between those two cases so that the cursor continues despite my query returning 0 results?
Thanks in advance.
DROP PROCEDURE IF EXISTS CURSOR_PLACEHOLDER;
DELIMITER $$
CREATE PROCEDURE CURSOR_PLACEHOLDER()
BEGIN
DECLARE FINISHED INTEGER DEFAULT 0;
DECLARE CURRENT_ROW_ID VARCHAR(256);
DEClARE CURRENT_ROW
CURSOR FOR
SELECT ID
FROM A
WHERE ID_P IN (SELECT ID_P
FROM A
GROUP BY ID_P
HAVING COUNT(ID_P) > 1);
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
OPEN CURRENT_ROW;
GET_ACTION:
LOOP
FETCH CURRENT_ROW INTO CURRENT_ROW_ID;
IF FINISHED = 1 THEN
LEAVE GET_ACTION;
END IF;
SET #CURRENT_P_ID := (SELECT ID_P FROM A WHERE ID = CURRENT_ROW_ID);
SELECT a.ID_U, a.ID_R, a.A, a.FROMDATE, a.TODATE
INTO #ID_U, #ID_R, #A, #FROM_DATE, #TO_DATE
FROM ASSIG a
WHERE a.ID_G = #CURRENT_P_ID; # <- Query that returns 0 rows and terminates the cursor
IF (#ID_U IS NOT NULL) THEN
SET #ASSIG_ID := GENERATE_ID();
INSERT INTO ASSIG
VALUES (#ASSIG_ID,
#G_ID,
#ID_U,
#ID_R,
#A,
#FROM_DATE,
#TO_DATE);
END IF;
UPDATE A
SET ID_P = #G_ID
WHERE ID = CURRENT_ROW_ID;
END LOOP GET_ACTION;
CLOSE CURRENT_ROW;
END
$$
DELIMITER ;

Can I use subquery in a user-defined function

I try to use subquery in mysql custom user-defined function I get an error so could u help me with one example.
Here is my code:
CREATE DEFINER=`root`#`localhost` FUNCTION `findsubName`(counts INT)
RETURNS varchar(255) CHARSET utf8
BEGIN
DECLARE result VARCHAR(500) DEFAULT NULL;
DECLARE v_name VARCHAR(200);
DECLARE finished INT(1) DEFAULT 0;
DECLARE my_cursor CURSOR FOR
SELECT id, (SELECT t_name FROM ajctb_titles b WHERE a.jt_id=b.t_id)
as tableName FROM ajctb_vacancies a limit counts;
DECLARE CONTINUE HANDLER
FOR NOT FOUND
SET finished = 1;
OPEN my_cursor;
calc_change: LOOP
FETCH my_cursor INTO v_name;
IF finished THEN
LEAVE calc_change;
END IF;
IF result<>'' THEN
SET result = CONCAT_WS(',',result,v_name);
ELSE
SET result = v_name;
END IF;
END LOOP calc_change;
CLOSE my_cursor;
RETURN result;
END
Error message:
Error Code: 1328. Incorrect number of FETCH variables
Error message: Error Code: 1328. Incorrect number of FETCH variables
Error messages attempt to tell you what the problem is. It is in the FETCH. Looking at the documentation:
13.6.6.3 Cursor FETCH Syntax
FETCH [[NEXT] FROM] cursor_name INTO var_name [, var_name] ...
This statement fetches the next row for the
SELECT statement associated with the specified cursor (which must be
open), and advances the cursor pointer. If a row exists, the fetched
columns are stored in the named variables. The number of columns
retrieved by the SELECT statement must match the number of output
variables specified in the FETCH statement.
https://dev.mysql.com/doc/refman/8.0/en/fetch.html
2 columns in your query:
SELECT
id
, (
SELECT
t_name
FROM ajctb_titles b
WHERE a.jt_id = b.t_id
)
AS tableName
means 2 variables are needed the FETCH
It hasn't even attempted the subquery yet.
Regarding that correlated subquery it could be a problem. When you use a subquery in the select clause like this it MUST return no more then one value. So you should use limit 1 if you continue with that subquery.
That subquery can be replaced with a join. e.g.
SELECT
id
, b.t_name AS tableName
FROM ajctb_vacancies a
LEFT JOIN ajctb_titles b ON a.jt_id = b.t_id
You may want to use an INNER JOIN if you must always have a non-null tablename returned.

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.

Copy one column to another, two seperate tables

I want to copy all values from one column in table A to another column in table B. The column has 100+ rows. I tried this:
UPDATE nds_product_lang pl
SET description_short = (
SELECT product_supplier_reference
FROM nds_product_supplier ps
WHERE ps.id_product = pl.id_product);
But what it returns is
#1242 - Subquery returns more than 1 row
It returns the same error even if I remove the WHERE condition. What am I doing wrong?
Try this
UPDATE nds_product_lang pl, nds_product_supplier ps
SET pl.description_short = ps.product_supplier_reference
WHERE ps.id_product = pl.id_product
You can use a store procedure:
See below example
CREATE DEFINER=`xxx`#`localhost` PROCEDURE `additem`()
BEGIN
declare no_record int default 0;
declare mycat varchar(45) default ''; //variable data destination
declare mycursor CURSOR FOR // this point your source table
select field-name FROM yourtable;
declare continue handler for not found
set no_record = 1;
open mycursor;
add_item: LOOP
FETCH mycursor INTO mycat;
IF no_record = 1 THEN
LEAVE add_item;
END IF;
-- build email list
insert into dest_table values(val-field1,mycat, ect..);
END LOOP add_item;
close mycursor;
this work perfectly :-)

Result consisted of more than one row error mysql

I am trying to get the next available id through a stored procedure but so far I cant make this to work. This is my stored procedure
DELIMITER $$
USE `devt`$$
DROP PROCEDURE IF EXISTS `updateid`$$
CREATE DEFINER=`root`#`%` PROCEDURE `updateid`(IN `tran_id` VARCHAR(15), IN `typ` VARCHAR(15), OUT `lid` VARCHAR(15))
BEGIN
DECLARE id VARCHAR(15);
SET id = '';
SELECT TranID FROM journal WHERE TranID = tranid INTO id;
read_loop: LOOP
SELECT TranID FROM journal WHERE TranID = id INTO id;
IF id = tran_id THEN
UPDATE journaltype SET journaltype.NextRefNbr = journaltype.NextRefNbr + 1 WHERE journaltype.JournalType = typ;
SELECT journaltype.NextRefNbr FROM journaltype WHERE journaltype.JournalType = typ INTO id;
ITERATE read_loop;
ELSE
SET lid= id;
LEAVE read_loop;
END IF;
END LOOP;
END$$
DELIMITER ;**strong text**
when i run the query CALL updateid('17000001','GJ', #test) i get an error Result consisted of more than one row
This error probably means that you either have multiple entries with the same TranID in table journal or multiple entries with the same JournalType in table journaltype.
When using the SELECT...INTO... syntax you have to be sure that the returned resultset will not consist of more than one rows.
ONE of your SELECT statement is returning more than one row you need to be more clear on results for example
Instead of using this :
SELECT journaltype.NextRefNbr FROM journaltype WHERE journaltype.JournalType = typ INTO id;
Use this :
SELECT journaltype.NextRefNbr FROM journaltype WHERE journaltype.JournalType = typ INTO id LIMIT 1;
this will resolve your error!.
Try to use in every SELECT statement if it is using no indexed column in WHERE clause