Nested Cursors in MYSQL, cursor not working as expected - mysql

I have created an SP in MYSQL to get values by date, by the sensor. My SP executes the inner cursor correctly but the outer cursor(1st cursor) is not executed. i.e. I only get 1 day of data, dateTable has a week's data.
CREATE PROCEDURE `Analysis`()
BEGIN
declare v_date datetime;
declare v_sensor varchar(50);
DECLARE datecursHandler,sensorCursHandler BOOLEAN DEFAULT FALSE;
Block1: BEGIN
declare datecursor CURSOR for
select distinct date from dateTable;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET datecursHandler = TRUE;
Open datecursor;
datecurs: loop
FETCH datecursor into date;
IF datecursHandler THEN
CLOSE datecursor;
LEAVE datecurs;
END IF;
Block2: BEGIN
declare sensorCursor CURSOR for
select distinct sensor from sensorTable ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET sensorcur = TRUE;
Open sensorCursor;
senscurs: loop
FETCH sensorCursor into sensor;
IF sensorcur THEN
SET sensorcur = False;
CLOSE sensorCursor;
LEAVE senscurs;
END IF;
Insert into temptable(
sensorValue,
DateID,
TimeID,
TotalCount,
TotalDistinctCount
)
SELECT
sensor AS sensorValue,
DATE_FORMAT(firstdate, '%Y%m%d') AS DateID,
HOUR(firstdate) + 1 AS TimeID,
COUNT(*) AS totalcount,
COUNT(DISTINCT sensor) AS sensordistinctcount
FROM
(SELECT
sensor AS sensor,
first_seen AS DeviceFirstSeen,
last_seen AS DeviceLastSeen,
DATE_FORMAT(FROM_UNIXTIME(first_seen), '%Y/%m/%d %k:%i:%s.%f') AS firstdate,
DATE_FORMAT(FROM_UNIXTIME(last_seen), '%Y/%m/%d %k:%i:%s.%f') AS lastdate,
FROM
sensorTable
INNER JOIN sensorTable2 ON sensorTable.ID = sensorTable2.ID
WHERE sensorTable.DeviceFirstSeen BETWEEN date_format(date_sub(date,interval 1 day),'%Y-%m-%d 15:00:00') AND date_format(date,'%Y-%m-%d 14:59:59')) a
GROUP BY DATE_FORMAT(firstdate, '%Y%m%d') , HOUR(firstdate) + 1;
end loop Maccurs;
END Block2;
END loop datecurs;
END Block1;
END
Can anyone please help me debug my code? I have researched but so far my code looks correct as per my research but doesn't work as expected.
Required Output:
Get counts of all the sensors for each day each hour that is selected from dateTable.

Try the below procedure, Since the issue might be incorrect closing of cursor.
CREATE PROCEDURE `Analysis`()
BEGIN
declare v_date datetime;
declare v_sensor varchar(50);
DECLARE datecursHandler,sensorCursHandler BOOLEAN DEFAULT FALSE;
Block1: BEGIN
declare datecursor CURSOR for
select distinct date from dateTable;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET datecursHandler = TRUE;
select distinct date from dateTable; #what is the result set you are getting?
Open datecursor;
datecurs: loop
FETCH datecursor into v_date;
IF datecursHandler THEN
LEAVE datecurs;
END IF;
Block2: BEGIN
declare sensorCursor CURSOR for
select distinct sensor from sensorTable ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET sensorCursHandler = TRUE;
Open sensorCursor;
senscurs: loop
FETCH sensorCursor into v_sensor;
IF sensorCursHandler THEN
SET sensorCursHandler = false;
LEAVE senscurs;
END IF;
Insert into temptable(
sensorValue,
DateID,
TimeID,
TotalCount,
TotalDistinctCount
)
SELECT
sensor AS sensorValue,
DATE_FORMAT(firstdate, '%Y%m%d') AS DateID,
HOUR(firstdate) + 1 AS TimeID,
COUNT(*) AS totalcount,
COUNT(DISTINCT sensor) AS sensordistinctcount
FROM
(SELECT
sensor AS sensor,
first_seen AS DeviceFirstSeen,
last_seen AS DeviceLastSeen,
DATE_FORMAT(FROM_UNIXTIME(first_seen), '%Y/%m/%d %k:%i:%s.%f') AS firstdate,
DATE_FORMAT(FROM_UNIXTIME(last_seen), '%Y/%m/%d %k:%i:%s.%f') AS lastdate,
FROM
sensorTable
INNER JOIN sensorTable2 ON sensorTable.ID = sensorTable2.ID
WHERE sensorTable.DeviceFirstSeen BETWEEN date_format(date_sub(date,interval 1 day),'%Y-%m-%d 15:00:00') AND date_format(date,'%Y-%m-%d 14:59:59')) a
GROUP BY DATE_FORMAT(firstdate, '%Y%m%d') , HOUR(firstdate) + 1;
end loop senscurs;
close sensorCursor;
END Block2;
END loop datecurs;
close datecursor;
END Block1;
END

Related

MySQL - Using SELECT INTO statement inside cursor in stored procedure

I am writing stored procedure in MySQL database using cursor to iterate on records. I am able to fetch records inside variables specified in FETCH statement, but when I use SELECT inside cursor, I am not able to store result in variables. I know its possible but can't find out why its not working in below MySQL code.
CREATE DEFINER=`root`#`localhost` PROCEDURE `add_monthly_leave_entitlement`
(IN `emp` INT(10), OUT `experience` INT(10), OUT `entitlement` DECIMAL(10,4)) DETERMINISTIC
BEGIN
DECLARE emp_no INT(10);
DECLARE current_month_exp INT(10);
DECLARE previous_month_end_exp INT(10);
DECLARE emp_status INT(10);
DECLARE done INT DEFAULT FALSE;
DECLARE monthly_entitlement decimal(8,4) DEFAULT 0;
DECLARE emp_cursor CURSOR FOR
SELECT e.`emp_number` AS emp_no,
TIMESTAMPDIFF( YEAR, e.`joined_date` , NOW( ) ) AS month_start_exp,
TIMESTAMPDIFF( YEAR, e.`joined_date`, LAST_DAY(NOW()- INTERVAL 1 MONTH) ) AS last_month_end_exp, e.`emp_status` AS emp_status
FROM `hs_hr_employee` AS e
WHERE e.`termination_id` is null AND e.`emp_number`=emp;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN emp_cursor;
read_loop : LOOP
FETCH emp_cursor INTO emp_no, current_month_exp, previous_month_end_exp, emp_status;
IF done THEN
LEAVE read_loop;
END IF;
/*Monthly entitlement as per experience*/
SELECT `monthly_entitlement` INTO monthly_entitlement
FROM `ohrm_leave_entitlement_conf`
WHERE `year_completion` = IF(previous_month_end_exp >4 , 5, previous_month_end_exp)
AND `leave_type_id` = 4;
SET experience = previous_month_end_exp;
SET entitlement = monthly_entitlement;
END LOOP;
CLOSE emp_cursor;
END
Not able to get value inside monthly_entitlement or entitlement, the value is always 0.0000.
I tried to run monthly entitlement query in separate procedure, it returns correct value. I have match data types of table column and variables.
Can somebody help me to understand what is wrong here ?
Avoid naming local variables as columns name, see Chapter 2 Restrictions on Stored Programs :: Name Conflicts within Stored Routines. Try changing the following:
...
-- DECLARE monthly_entitlement decimal(8,4) DEFAULT 0;
DECLARE _monthly_entitlement decimal(8,4) DEFAULT 0;
...
/*Monthly entitlement as per experience*/
-- SELECT `monthly_entitlement` INTO monthly_entitlement
SELECT `monthly_entitlement` INTO _monthly_entitlement
...
-- SET entitlement = monthly_entitlement;
SET entitlement = _monthly_entitlement;
...
As suggested by others the Name of the local variable and the column name should be different, So here the changes
CREATE DEFINER=`root`#`localhost` PROCEDURE `add_monthly_leave_entitlement`
(IN `emp` INT(10), OUT `experience` INT(10), OUT `entitlement` DECIMAL(10,4)) DETERMINISTIC
BEGIN
DECLARE v_emp_no INT(10);
DECLARE v_current_month_exp INT(10);
DECLARE v_previous_month_end_exp INT(10);
DECLARE v_emp_status INT(10);
DECLARE done INT DEFAULT FALSE;
DECLARE v_monthly_entitlement decimal(8,4) DEFAULT 0;
DECLARE emp_cursor CURSOR FOR
SELECT e.`emp_number` AS emp_no,
TIMESTAMPDIFF( YEAR, e.`joined_date` , NOW( ) ) AS month_start_exp,
TIMESTAMPDIFF( YEAR, e.`joined_date`, LAST_DAY(NOW()- INTERVAL 1 MONTH) ) AS last_month_end_exp, e.`emp_status` AS emp_status
FROM `hs_hr_employee` AS e
WHERE e.`termination_id` is null AND e.`emp_number`=emp;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN emp_cursor;
read_loop : LOOP
FETCH emp_cursor INTO v_emp_no, v_current_month_exp, v_previous_month_end_exp, v_emp_status;
IF done THEN
LEAVE read_loop;
END IF;
/*Monthly entitlement as per experience*/
SELECT `monthly_entitlement` INTO v_monthly_entitlement
FROM `ohrm_leave_entitlement_conf`
WHERE `year_completion` = IF(v_previous_month_end_exp >4 , 5, v_previous_month_end_exp)
AND `leave_type_id` = 4;
SET experience = v_previous_month_end_exp;
SET entitlement = v_monthly_entitlement;
END LOOP;
CLOSE emp_cursor;
END

Want to return data set from this cursor ,stored procedure

i am working on this stored procedure , can any body please help me to get data set from this procedure .
*Create a stored procedure with a variable entry called list_g
This is the procedure (see attached):
and then:
*=at
SET *list = "";
CALL getNodes(*list);
select *list;
Here is the Stored Procedure :
BEGIN
DECLARE done1, done2 BOOLEAN DEFAULT FALSE;
DECLARE row1 varchar(900) default "";
DECLARE parentId INTEGER;
DECLARE cur1 CURSOR FOR SELECT idIncidence FROM incidence
WHERE DATE(dateTimeIn_dt)='2018-01-18' and hour(TIME(dateTimeIn_dt))
BETWEEN '08' and '15';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = TRUE;
open cur1;
loop1: LOOP
FETCH cur1 INTO parentId;
IF done1 THEN
CLOSE cur1;
LEAVE loop1;
END IF;
set list_g=CONCAT(parentId,";",list_g);
BLOCK1 : BEGIN
DECLARE cur2 CURSOR FOR SELECT
a.idIncidence,DATE_FORMAT(dateTimeIn_dt,'%H:%i:%s') as dateTimeIn_dt,0
as conteo, a.classification_str,measure_n,a.keyNode_str_fk,(
SELECT GROUP_CONCAT( nameStation_str SEPARATOR ', ') AS
idStation
FROM (
SELECT nameStation_str,
( 6371 * acos(cos(radians((SELECT lat from incidence where
idIncidence =#parentId))) * cos(radians(lat_str)) *
cos(radians(lon_str) - radians((SELECT lon from incidence where
idIncidence =#parentId))) + sin(radians((SELECT lat from incidence
where idIncidence =#parentId))) * sin(radians(lat_str))) )
AS distance
FROM station HAVING distance < 0.01 ORDER BY distance
) as s
)as
idStation_fk,kp.kindProtective_str,m.nameModel_str
,pa.keyArea_n,kp.idKinProtector FROM incidence a
JOIN node n on a.keyNode_str_fk=n.keyNode_str
JOIN protective pr on n.idProtective_fk=pr.idProtective
JOIN kindProtector kp on pr.idKindProtector_fk=kp.idKinProtector
JOIN model m on pr.idModel_fk=m.idModel
JOIN station st on a.idStation_fk =st.idStation
JOIN branch br on st.idBranch_fk=br.idBranch
JOIN ship sh on br.idShip_fk=sh.idShip
JOIN protectiveArea pa on n.idProtectiveArea=pa.idProtectiveArea
WHERE DATE(dateTimeIn_dt)='2018-01-18' and
hour(TIME(dateTimeIn_dt)) BETWEEN '08' and '15';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = TRUE;
OPEN cur2;
loop2 : LOOP
if done2 THEN
CLOSE cur2;
SET done2 = FALSE;
LEAVE loop2;
END IF;
END LOOP loop2;
END BLOCK1;
END loop loop1;
END
i need to return all the data set from the query
Please help me in this problem. Here are my Tables Name :
Branch , brand ,group_node ,incidence ,kindProtector,model,node,plant,protective,protectiveArea,protectiveGroup,sensor,shift,ship,station,user .

MYSQL loop in function not working

I have a mysql function which will change records accordingly . But loop only execute for one time and leaves loop with this condition. "IF v_finished =1 THEN
LEAVE get_stock;
END IF;"
However its is supposed to execute multiple time. Like in my test case 3 times
BEGIN
DECLARE P_stock int(11);
DECLARE P_product int(11);
DECLARE V_From_warehouse int(11);
DECLARE V_To_warehouse int(11);
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE V_To_warehouse_stock int(11);
DECLARE V_From_warehouse_stock int(11);
declare cur1 cursor for
SELECT material_transfer_details.product_id , material_transfer_details.quantity FROM
material_transfers,
material_transfer_details
WHERE
material_transfers.id = material_transfer_details.mtm_id
AND
material_transfers.status = 'Y'
AND
material_transfers.id = V_MTM_id;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
SELECT warehouse_from INTO V_From_warehouse FROM material_transfers WHERE id =V_MTM_id;
SELECT warehouse_to INTO V_To_warehouse FROM material_transfers WHERE id =V_MTM_id;
OPEN cur1;
get_stock: LOOP
IF v_finished =1 THEN
LEAVE get_stock;
END IF;
fetch cur1 into P_product , P_stock;
SELECT quantity INTO V_To_warehouse_stock from stocks where warehouse_id = V_To_warehouse and product_id = P_product;
SELECT quantity INTO V_From_warehouse_stock from stocks where warehouse_id = V_From_warehouse and product_id = P_product;
IF (V_To_warehouse_stock IS NOT NULL)
THEN
UPDATE
stocks SET quantity = quantity - P_stock
WHERE
warehouse_id = V_to_warehouse
AND
product_id = P_product;
ELSE
INSERT INTO stocks(product_id , warehouse_id , quantity ,status, created_datetime , updated_datetime) values
(P_product , V_to_warehouse , 0-P_stock , 'Y', sysdate() , sysdate());
END IF;
IF (V_From_warehouse_stock IS NOT NULL)
THEN
UPDATE
stocks SET quantity = quantity + P_stock
WHERE
warehouse_id = V_from_warehouse
AND
product_id = P_product;
ELSE
INSERT INTO stocks(product_id , warehouse_id , quantity ,status, created_datetime , updated_datetime) values
(P_product , V_from_warehouse , P_stock , 'Y', sysdate() , sysdate());
END IF;
SET P_stock = 0;
SET P_product = 0;
END LOOP get_stock;
CLOSE cur1;
UPDATE material_transfers SET Status = 'N' WHERE id= V_MTM_id;
UPDATE material_transfer_details SET Status = 'N' WHERE mtm_id = V_MTM_id;
return '00000';
END
Two things:
First, change your code.
get_stock: LOOP
SET v_finished = FALSE;
fetch cur1 into P_product , P_stock;
IF v_finished =1 THEN
LEAVE get_stock;
END IF;
Since you are doing other things that could trip the handler, reset v_finished, then fetch from the cursor, and only then test whether to leave the loop.
As written, if you hadn't tripped the handler prematurely, you would have been testing in entirely the wrong place and would have stayed in the loop too long.
Next... be sure you understand SELECT ... INTO. I don't think it does quite what you think it does.
A scalar subquery is a much safer solution:
SET V_To_warehouse_stock = (SELECT quantity from stocks where warehouse_id = V_To_warehouse and product_id = P_product);
If SELECT ... INTO returns no rows, the variable's value does not change. It retains its former value, if one exists, and that is rarely what you expect.
Spooky action at a distance like this is best avoided, and it's an easy trap to fall into, in a loop.
See examples of this effect at https://dba.stackexchange.com/a/35207/11651.

Error 1248: Every derived table must have its own alias

Please see my code, below. I get the error:
Error 1248: Every derived table must have its own alias
What am I doing wrong?
CREATE PROCEDURE `insert_seance` (IN compared_id INT)
BLOCK1: BEGIN
CREATE TEMPORARY TABLE log
LIKE seance_log;
CREATE TEMPORARY TABLE compared_log
LIKE seance_log;
BLOCK2: BEGIN
DECLARE done, inner_done BOOLEAN DEFAULT FALSE;
DECLARE diff BOOL DEFAULT TRUE;
DECLARE current_id, se_id, q_id, ans INT;
DECLARE compared CURSOR FOR
SELECT seance_id, question_id, answer FROM seance_log
WHERE seance_id = compared_id;
DECLARE seance CURSOR FOR
SELECT seance_id FROM seances
WHERE seance_id <> compared_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN compared;
WHILE NOT done DO
FETCH compared INTO se_id, q_id, ans;
INSERT INTO compared_log (seance_id, question_id, answer)
VALUES (se_id, q_id, ans);
END WHILE;
CLOSE compared;
SET done = FALSE;
OPEN seance;
WHILE NOT done DO
FETCH seance INTO current_id;
DELETE FROM log;
BLOCK3: BEGIN
DECLARE answers CURSOR FOR
SELECT seance_id, question_id, answer FROM seance_log
WHERE seance_id = current_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET inner_done = TRUE;
DELETE FROM log;
OPEN answers;
WHILE NOT inner_done DO
FETCH answers INTO se_id, q_id, ans;
INSERT INTO log (seance_id, question_id, answer)
VALUES (se_id, q_id, ans);
END WHILE;
CLOSE answers;
SELECT IF ((SELECT COUNT(*) FROM (SELECT 'log' AS `set`, l.*
FROM log l
WHERE ROW(l.seance_id, l.question_id, l.answer) NOT IN
(SELECT * FROM compared_log)
UNION ALL
SELECT 'compared_log' AS `set`, cl.*
FROM compared_log cl
WHERE ROW(cl.seance_id, cl.question_id, cl.answer) NOT IN
(SELECT * FROM log)
)),FALSE,TRUE) INTO diff;
CASE WHEN diff = FALSE
THEN DELETE FROM seance_log
WHERE seance_id = compared_id;
UPDATE seances SET count = count + 1
WHERE seance_id = compared_id;
END CASE;
SET done = NOT diff;
END BLOCK3;
END WHILE;
CLOSE seance;
END BLOCK2;
END BLOCK1

Mysql Function - not sure where to start

I would like to write a function in Mysql that from a given product code return a formatted string
Here is an example of the calls I need to manually make now to get the result I want.
SELECT p.productcategoryid from products p where (isnull(p.endeffdt) or (p.endeffdt = '0000-00-00') or (p.endeffdt > now())) and p.code='T29R66N1';
T29R66NQ is the product code I need the full path for - the above call returns '38' as the category ID.
I then perform the following select based on the result from above
SELECT name,parentid,productcategorypath FROM productcategory WHERE recid = '38';
This returns
name->Built-In Hobs
parentid->7
productcategorypath=222,7,38
Using that result I then
SELECT name,parentid,productcategorypath FROM productcategory WHERE recid = '7';
giving me
name->Built-In
parentid->222
productcategorypath=222,7
and again, I then do
SELECT name,parentid,productcategorypath FROM productcategory WHERE recid = '222';
which in turn gives me
name->Kitchen & Home Appliances
parentid->0
productcategorypath=222
I stop there because parentid = 0 (it may go on for more iterations but will always end with parent id of 0) but i need the results from the last 3 selects to give me the following string
Kitchen & Home Appliances > Built-In > Built-In Hobs
I would like a mysql function whereby I can use it like
select getpath(code) from products where code='T29R66N1'
Any help would be appreciated.
EDIT:
I managed to figure it myself - here is my function
DROP FUNCTION IF EXISTS mydb.getpath;
CREATE FUNCTION mydb.getpath (itemid VARCHAR(20))
RETURNS varchar(255)
BEGIN
DECLARE path_name varchar(255);
DECLARE tmp_name varchar(255);
DECLARE tmp_parentid INT;
DECLARE tmp_parentid1 INT;
SELECT p.productcategoryid INTO tmp_parentid from products p where (isnull(p.endeffdt) or (p.endeffdt = '0000-00-00') or (p.endeffdt > now())) and p.code=itemid;
myloop:LOOP
SELECT name,parentid INTO tmp_name,tmp_parentid1 FROM productcategory WHERE recid = tmp_parentid;
SET path_name = concat_ws(' > ', tmp_name,path_name);
IF tmp_parentid1!=0 THEN
SET tmp_parentid = tmp_parentid1;
ITERATE myloop;
ELSE
LEAVE myloop;
END IF;
END LOOP;
RETURN path_name;
END;
DROP FUNCTION IF EXISTS mydb.getpath;
CREATE FUNCTION mydb.getpath (itemid VARCHAR(20))
RETURNS varchar(255)
BEGIN
DECLARE path_name varchar(255);
DECLARE tmp_name varchar(255);
DECLARE tmp_parentid INT;
DECLARE tmp_parentid1 INT;
SELECT p.productcategoryid INTO tmp_parentid from products p where (isnull(p.endeffdt) or (p.endeffdt = '0000-00-00') or (p.endeffdt > now())) and p.code=itemid;
myloop:LOOP
SELECT name,parentid INTO tmp_name,tmp_parentid1 FROM productcategory WHERE recid = tmp_parentid;
SET path_name = concat_ws(' > ', tmp_name,path_name);
IF tmp_parentid1!=0 THEN
SET tmp_parentid = tmp_parentid1;
ITERATE myloop;
ELSE
LEAVE myloop;
END IF;
END LOOP;
RETURN path_name;
END;