MYSQL Stored Procedure only updates on record - mysql

MYSQL Stored Procedure only updates one record when it should update all the ids in the WHERE id IN clause. When I run the SELECT and UPDATE outside of the stored procedure it works fine. Any suggestions?
DELIMITER $$
CREATE PROCEDURE update_ids(IN source int(10),IN target int(10))
BEGIN
DECLARE idList varchar(5000) DEFAULT NULL;
SET idList = (SELECT GROUP_CONCAT(id SEPARATOR ', ') FROM myTable ii WHERE ii.generic_id = source);
UPDATE myTable i SET i.generic_id = target WHERE i.id IN (idList);
END$$
DELIMITER ;
Then I call it by -->
CALL update_generic_ids(63, 1258);
Update : 1 row effected.
Thanks in advance,

once try below chunk of code, hope it will solve your problems!
If any question reach me!
DELIMITER $$
CREATE PROCEDURE update_ids(IN source int(10),IN target int(10))
BEGIN
DECLARE idList varchar(5000) DEFAULT NULL;
--SET idList = (SELECT GROUP_CONCAT(id SEPARATOR ', ') FROM myTable ii WHERE ii.generic_id = source);
UPDATE myTable i SET i.generic_id = target WHERE i.id IN (
SELECT id FROM myTable ii WHERE ii.generic_id = source
);
END$$
DELIMITER ;

Actually, as per my comments, I think is may be the answer..
There's a world of difference between these two queries:
UPDATE a SET b = c WHERE d IN ('1,2,3,4')
UPDATE a SET b = c WHERE d IN (1,2,3,4)
I'd recommend you amend your procedure so you're not storing your list of ids in a string variable. Actually you shouldn't need to store anything, just put the list generated by the select into the update:
UPDATE myTable i SET i.generic_id = target
WHERE i.id IN
(SELECT id SEPARATOR FROM myTable ii WHERE ii.generic_id = source);

Related

why mysql procedure return null

I have a simple mysql procedure which must return an query string. But, it returns almost every time QueryResult (column name) as <null> value.
create procedure return_table_rename_query(
IN targetTable VARCHAR(100),
IN tblPrefix VARCHAR(100)
)
BEGIN
SET #returnQuery = CONCAT('SELECT "MYSQLIMPORT can not rename table for target ', #targetTable, '";');
SET #totalRows = (SELECT COUNT(*) FROM table);
if IFNULL(#totalRows, 0) > 0
then
SET #returnQuery = CONCAT('drop table if exists table_name.', ...);
end if;
SELECT #returnQuery AS 'QueryResult';
end;
#targettable is not the same variable as targettable - you are mixing user defined variables and parameter variables and it seems likely that #targettable is null and if any element in a concat is null then the result is null.
Please read How to declare a variable in MySQL?

How to separate a string and re build it

Separating String list and replacing same list with new values in mysql
I have following data in my table_1 Table
table_1(Currently saved structure)
code value
12_A ["A","B","C","D"]
12_B ["E","F","G","H"]
12_3 ["I","J","K","L"]
But each code have different values with different description. like::
code value description
12_A A Apple
12_A B Ball
12_A C Cat
12_A D Dog
12_B E Eagle
12_B F Flag
. . .
. . .
. . .
I have to Separate the value list from table_1 and
need to save again in same table i.e table_1(in this structure)::
code value
12_A ["Apple","Ball","Cat","Dog"]
12_B ["Eagle","Flag",.......]
12_3 [......................]
You can use GROUP_CONCAT() :
UPDATE Table1 s
SET s.value = (SELECT t.code,CONCAT('["',
GROUP_CONCAT(t.description ORDER BY t.description SEPARATOR '","'),
']')
FROM Table_With_val t
WHERE t.code = s.code
AND s.value LIKE CONCAT('%"',t.value,'"%'))
You didn't provide any conclusive information, I assumed the second data sample you provided is an existing table, and table1 is the table you want to update.
NOTE: This is a bad DB structure! it would most defiantly cause problem in the future especially when required to make joins . I strongly advise you to normalize your data and store each description and value in its own record.
you can create a function in which you can pass your string list as parameter in case of your example ["A","B","C","D"] will be the parameter. The function will break down the string and will concatenate the descriptions according. The example of the function you can use is given below:
DELIMITER $$
DROP FUNCTION IF EXISTS codeToDesc$$
CREATE FUNCTION codeToDesc(commaSeperatedCodeList TEXT) RETURNS TEXT CHARSET utf8
BEGIN
DECLARE finalString TEXT;
DECLARE inputCodeList TEXT;
DECLARE codeName VARCHAR(255);
DECLARE codecount BIGINT(5);
SET finalString='';
SET inputCodeList = REPLACE(REPLACE(REPLACE(commaSeperatedCodeList,'[',''),']',''),'"','');
DROP TEMPORARY TABLE IF EXISTS test.code_table;
DROP TEMPORARY TABLE IF EXISTS test.code_count;
CREATE TEMPORARY TABLE test.code_table (CODE VARCHAR(255));
CREATE TEMPORARY TABLE test.code_count (countNo BIGINT(11));
INSERT INTO test.code_count(countNo) SELECT(LENGTH(inputCodeList)-LENGTH(REPLACE(inputCodeList,',','')) + 1);
BEGIN
DECLARE table_cursor CURSOR FOR SELECT countNo FROM test.code_count;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET codecount = (SELECT countNo FROM test.code_count ORDER BY countNo ASC LIMIT 1);
OPEN table_cursor;
readLoop1: LOOP
FETCH table_cursor INTO codecount;
IF codecount=0 THEN
LEAVE readLoop1;
END IF;
SET codeName=(SELECT SUBSTRING_INDEX(inputCodeList,',',1));
INSERT INTO test.code_table(CODE) SELECT codeName;
SET inputCodeList=(SELECT TRIM(BOTH ',' FROM REPLACE(inputCodeList,codeName,'')));
INSERT INTO test.code_count(countNo) SELECT codecount-1;
SET codeName='';
END LOOP;
CLOSE table_cursor;
END;
-- use your code and description here, i guess those should be fixed
SELECT CONCAT('["',REPLACE(GROUP_CONCAT(CASE WHEN CODE='A' THEN 'Apple'
WHEN CODE = 'B' THEN 'Ball'
WHEN CODE = 'C' THEN 'Cat'
WHEN CODE = 'D' THEN 'Dog'
ELSE '' END),',','","'),'"]') INTO finalString FROM test.code_table;
RETURN finalString;
END$$
DELIMITER ;
Try this, let me know if you any issue occurred.

how to handle \ in query mysql

I have a procedure in which I am making query as string then prepare query and execute.
Here is the procedure
CREATE DEFINER=`root`#`%` PROCEDURE `dim_add_customer`(
IN _customer_id BIGINT(20) ,
IN _first_name VARCHAR(50) ,
)
BEGIN
SET #_query := CONCAT('first_name = "',_first_name,'"');
SET #_query := CONCAT('UPDATE customer_detail SET ',#_query,' WHERE customer_id = ',_customer_id);
PREPARE stmt FROM #_query;
END$$
DELIMITER ;
Now when I call
call dim_add_customer(1,'abc\\')
Then there is issue in creating string query.The query it made
UPDATE customer_detail SET first_name = "abc\" WHERE customer_id = 1
is there any best solution to solve this ?
You shouldn't build the queries by concat.
You should use the parameters in the query like
SET #_query="UPDATE customer_detail
SET first_name=#_first_name
WHERE customer_id = #_customer_id"
I'm not sure if you can declare your variables directly from the input parameters like
CREATE DEFINER=`root`#`%` PROCEDURE `dim_add_customer`(
IN #_customer_id BIGINT(20) ,
IN #_first_name VARCHAR(50) ,
)
or you have to
SET #_customer_id = _customer_id
SET #_first_name = _first_name
CAVEAT: I'm used to the MsSql-way of creating procedures with variables; I might have misunderstood something, but at least creating sql by concat should be your last resort.
Creating queries by concat is the equivalent of
x=1
q=concat("y=",x,"+2")
eval (q)
instead of
x=1
y=x+2

SELECT query to filter records using optional user-supplied parameters

I am writing a procedure to fatch record from database table based on filter condition. Now as user may apply filter or may not. So based on that filter criteria will get applied.
i.e. If filter parameter will contain any value than only it will apply condition not.
Note: I can achieve it through if.. else.. condition and execute different query altogether separately to get my desire output. But what I am trying to ask is, is there any better approach for it rather than if.. else..
Example ( here i_status_id, i_category_id may contain any value or may not)
DELIMITER $$
DROP PROCEDURE IF EXISTS `FilterRecord`$$
CREATE PROCEDURE `FilterRecord`(IN i_status_id BIGINT,IN i_category_id BIGINT)
BEGIN
SELECT
Item_backlog.backlog_id AS backlog_id,
Item_backlog.title AS backlog_name,
Item_backlog_Group.title AS group_Name,
Item_backlog.description AS description,
Item_backlog.est_start_date AS estimated_start_date,
Item_backlog.est_end_date AS estimated_end_date,
Item_backlog.actual_start_date AS actual_start_date,
Item_backlog.actual_end_date AS actual_end_date,
Item_backlog.estimated_hours AS estimated_hours,
Item_backlog.actual_hours AS actual_hours,
Item_backlog.status_id AS status_id,
APV_Status.name AS status_name ,
Item_backlog.priority_id AS priority_id,
APV_Priority.name AS priority_name,
Item_backlog.rank AS rank,
Item_backlog.isActive AS isActive,
Item_backlog.created_by_id AS created_by_id,
Item_backlog.created_on AS created_on,
Item_backlog.modified_by_id AS modified_by_id,
Item_backlog.modified_on AS modified_on
FROM
Item_backlog
INNER JOIN
ApplicationParamValue AS APV_Status ON (Item_backlog.status_id=APV_Status.appParamValueId)
INNER JOIN
ApplicationParamValue AS APV_Priority ON (Item_backlog.Priority_Id=APV_Priority.appParamValueId)
INNER JOIN
Item_backlog_group AS Item_backlog_Group ON (Item_backlog.backlog_group_id=Item_backlog_Group.backlog_group_id)
WHERE
Item_backlog.status_id=i_status_id
AND
Item_backlog.category_id=i_category_id
END$$
DELIMITER ;
Assuming that you have a defined value for indicating when a parameter is not specified (e.g. I've used NULL below to indicate that the parameter is not supplied), what you can do is the following to apply the parameter conditionally.
CREATE PROCEDURE `FilterRecord`(IN i_status_id BIGINT,IN i_category_id BIGINT)
BEGIN
SELECT
Item_backlog.backlog_id AS backlog_id,
-- ...
FROM
Item_backlog
INNER JOIN
-- ..
WHERE
(Item_backlog.status_id=i_status_id OR i_status_id IS NULL)
AND
(Item_backlog.category_id=i_category_id OR i_category_id IS NULL);
END
Note however that the performance of doing this can be degraded.
SqlFiddle example here - I've assumed MySql, btw, as per your primary tag and backticks.
Try below it will apply filter criteria based on parameter passed:
DELIMITER $$
DROP PROCEDURE IF EXISTS `FilterRecord`$$
CREATE PROCEDURE `FilterRecord`(IN i_status_id BIGINT,IN i_category_id BIGINT)
BEGIN
DECLARE Var_status_id VARCHAR(200);
DECLARE Var_category_id VARCHAR(200);
DECLARE andCondition VARCHAR(200);
IF i_status_id<>0 THEN
SET Var_status_id=CONCAT(' (Item_backlog.status_id = ', i_status_id,')');
SET andCondition ='AND';
ELSE
SET Var_status_id="";
SET andCondition ='';
END IF;
IF i_category_id<>0 THEN
SET Var_category_id=CONCAT(andCondition,' (Item_artifact_category_mapping.category_id = ', i_category_id,')');
ELSE
SET Var_category_id="";
END IF;
SET #SQL := CONCAT('SELECT
Item_backlog.backlog_id AS backlog_id,
Item_backlog.title AS backlog_name,
Item_backlog_Group.title AS group_Name,
Item_backlog.description AS description,
Item_backlog.est_start_date AS estimated_start_date,
Item_backlog.est_end_date AS estimated_end_date,
Item_backlog.actual_start_date AS actual_start_date,
Item_backlog.actual_end_date AS actual_end_date,
Item_backlog.estimated_hours AS estimated_hours,
Item_backlog.actual_hours AS actual_hours,
Item_backlog.status_id AS status_id,
APV_Status.name AS status_name ,
Item_backlog.priority_id AS priority_id,
APV_Priority.name AS priority_name,
Item_backlog.rank AS rank,
Item_backlog.isActive AS isActive,
Item_backlog.created_by_id AS created_by_id,
Item_backlog.created_on AS created_on,
Item_backlog.modified_by_id AS modified_by_id,
Item_backlog.modified_on AS modified_on
FROM
Item_backlog
INNER JOIN
ApplicationParamValue AS APV_Status ON (Item_backlog.status_id=APV_Status.appParamValueId)
INNER JOIN
ApplicationParamValue AS APV_Priority ON (Item_backlog.Priority_Id=APV_Priority.appParamValueId)
INNER JOIN
Item_backlog_group AS Item_backlog_Group ON (Item_backlog.backlog_group_id=Item_backlog_Group.backlog_group_id)
WHERE', Var_status_id, Var_category_id);
PREPARE stmt FROM #SQL;
EXECUTE stmt;
END$$
DELIMITER ;

MySQL stored procedure pass select as parameter

could you please give me an advice how to CALL prcd with SELECT results? Or advice me pls better solution.. I am open minded to all working solution
I have a procedure to control inserting data ...
CREATE PROCEDURE control_insert (
)
And I need to pass data from SELECT results to procedure ...
SELECT t.c1, t.c2
FROM table t1
LEFT JOIN other_table t2
ON t1.id = t2.id
WHERE 1=1
The point is, I need to get some data via SELECT (around 6 tables joined to the base table) and I need to do control for each row before insert.. each row should meet some conditions .. if it doesn't meet them, it should just skip it and process next one ...
The procedure should look like:
CREATE PROCEDURE control_insert (
IN v_c1 INT,
IN v_c2 INT
)
BEGIN
IF v_c1 > 1 THEN
INSERT INTO controlled_table (id, type) VALUES (v_c1, v_c2);
ELSE
/* do nothing */
END IF;
END;
CALL control_insert ( SELECT .... );
Could you help me with that? Is there any possibility to do this via MySQL? I can write a PERL skript, but I want to avoid this type of solution ... I just one to do it only in MySQL way
Thank you
EDIT1: I need to check if ID of the SELECT result and LABEL is already in this table for specific date ... this code above is only an example to demonstrate the situation
SOLUTION
I've found the solution ... so for the other visitors:
calling procedure:
CALL controlInsert();
procedure body:
CREATE PROCEDURE controlInsert()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_id INT;
DECLARE v_id_dupl INT;
DECLARE v_label INT;
DECLARE v_date DATE;
DECLARE v_type VARCHAR(100);
DECLARE v_category VARCHAR(255);
DECLARE v_user VARCHAR(255);
DECLARE v_country VARCHAR(255);
DECLARE c1 CURSOR FOR SELECT id, label, date, type, category, user, country FROM t1 LEFT JOIN ... /* whole select with 6 joins ended by ; */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
## open cursor
OPEN c1;
## loop through the cursor
read_loop: LOOP
## fetch cursor into variables
FETCH c1 INTO v_id , v_label, v_date, v_type, v_category, v_user, v_country;
## check if there is any record
IF done THEN
LEAVE read_loop;
END IF;
## get count of existing records
SELECT count(*) INTO v_id_dupl
FROM
WHERE 1=1
AND id = v_id
AND label= v_label
AND date = v_date;
## if v_id_dupl = 0 => no rows found (ok to load)
IF (v_id_dupl = 0) THEN
INSERT INTO target_table (id, label, date, type, category, user, country)
VALUES (v_id , v_label, v_date, v_type, v_category, v_user, v_country);
END IF;
END LOOP;
CLOSE c1;
END
If that is all your stored procedure is doing, then you don't actually need it. You can do the whole thing in a single statement:
INSERT INTO controlled_table (id, type)
SELECT t.c1, t.c2
FROM table t1
LEFT JOIN other_table t2 ON t1.id = t2.id
WHERE something = somethingElse
AND t.c1 > 1
Essentially, I've just combined your original query with the INSERT statement in your procedure.
If your procedure is more complex and needs to do multiple operations on each row, then you should look into using a cursor.