Mysql Function - not sure where to start - mysql

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;

Related

Nested Cursors in MYSQL, cursor not working as expected

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

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.

1172 - Result consisted of more than one row in mysql

How can I solve this problem (Result consisted of more than one row in mysql)
DROP PROCEDURE IF EXISTS `doMarksApplication`;
CREATE PROCEDURE `doMarksApplication`(
in kuser varchar(20),
out idpro int(11))
SP:BEGIN
declare no_more_rows int default FALSE;
declare total_marks decimal(10,2) default 0;
declare idfor int(11) default 0;
declare sskod int(5) default getCurSession();
declare bdata int(5) default 0;
declare nopmh varchar(20);
# Data PB [Permohonan Baru] DM [Proses Pemarkahan]
declare cur1 cursor for
select ind_nopmh from pinduk
left join pprses on pro_nopmh = ind_nopmh
where ind_sskod = sskod and
concat(pro_stats,pro_statp) in ('PB','DM') and
not exists (select mar_idnum from pmrkah where mar_nopmh = ind_nopmh)
order by ind_nopmh;
declare continue handler for not found set no_more_rows = TRUE;
begin
select count(ind_nopmh) into bdata
from pinduk
left join pprses on pro_nopmh = ind_nopmh
where ind_sskod = sskod and
concat(pro_stats,pro_statp) in ('PB','DM') and
not exists (select mar_idnum from pmrkah where mar_nopmh = ind_nopmh);
end;
begin
select count(for_idnum) into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
end;
if idfor = 1 and sskod <> 0 then
begin
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
end;
begin
insert into pprmar
(pma_tkmla,pma_msmla,pma_puser,pma_sskod,pma_idfor,pma_bdata)
values
(curdate(),curtime(),kuser,sskod,idfor,bdata);
end;
begin
select last_insert_id() into idpro;
end;
open cur1;
LOOP1:loop
fetch cur1 into nopmh;
if no_more_rows then
close cur1;
leave LOOP1;
end if;
begin
call getMarksAnakPerak(nopmh,#total_perak);
call getMarksAkademik(nopmh,#total_akdmk);
call getMarksSosioekonomi(nopmh,#total_sosio);
end;
set total_marks = #total_perak + #total_akdmk + #total_sosio;
begin
insert into pmrkah
(mar_idpro,mar_nopmh,mar_idfor,mar_perak,mar_akdmk,mar_sosio,mar_total)
values
(idpro,nopmh,idfor,#total_perak,#total_akdmk,#total_sosio,total_marks);
end;
begin
update pprses
set pro_stats = 'D',
pro_statp = 'M',
pro_tkmsk = curdate(),
pro_msmsk = curtime(),
pro_kuser = kuser
where pro_nopmh = nopmh;
end;
end loop;
begin
update pprmar
set pma_tktmt = curdate(),
pma_mstmt = curtime()
where pma_idnum = idpro;
end;
end if;
END;
i have been programming in mysql for 15 years and this is easily the most confusing stored procedure i have ever seen.
None the less, one possible place for your issue is here
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
I know it does not seem to be the reason but without knowing the content of the other three stored procedures you are calling this is the only candidate. You should add a limit 1 to it, and to every select into statement that reads from a table (i.e. not a sum() or a count() etc...) as that would always have the potential to cause the error you are seeing.
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg limit 1;
In addition, you should comment out the three stored procedure calls and see if the error goes away. My guess is that the issue is one of those stored procedures due to a select into similar to above has more than one row in the result set but does not use limit 1 and does not filter properly.

MySQL Cursor Fetch not working

I have the following stored procedure that is meant to implement Dijkstra's shortest path algorithm:
CREATE PROCEDURE `Dijkstras`(IN `pids` VARCHAR(512), IN `startP` VARCHAR(8), IN `endP` VARCHAR(8), OUT `dist` DECIMAL(20,10), OUT `eset` VARCHAR(1024))
BEGIN
DECLARE currentP VARCHAR(4);
DECLARE finished INT DEFAULT 0;
DECLARE pt_from, pt_to int;
DECLARE pt_dist decimal(20,10);
DECLARE done INT DEFAULT 0;
DECLARE cur2 CURSOR FOR
select F.id as `from`, T.id as `to`, dist(F.lat, F.lng, T.lat, T.lng)
as dist
from sampledata F, sampledata T
where F.id < T.id and
find_in_set(convert(F.id, char(10)), pids) and
find_in_set(convert(T.id, char(10)), pids)
order by dist;
DECLARE CONTINUE HANDLER FOR not found SET done = 1;
SET currentP= startP;
SET eset = '';
SET dist = 0;
SET done=0;
OPEN cur2; -- this finds pariwise distances in miles.
REPEAT
FETCH cur2 INTO pt_from, pt_to, pt_dist;
SET dist= dist+pt_dist;
SET eset= CONCAT(eset, ',');
IF(currentP=pt_from OR currentP=pt_to) AND
(IN_SET(pt_from,pids) AND IN_SET(pt_to,pids)) THEN
BEGIN
SET dist= dist+ pt_dist;
SET pids= REMOVE_MEMBER(currentP, pids);
SET eset = concat(eset, ',', concat(pt_from, ':', pt_to));
IF left(eset, 1) = ',' then
SET eset = substring(eset, 2); -- remove extra comma.
END IF;
IF currentP=pt_from THEN
SET currentP=pt_to;
ELSE
SET currentP=pt_from;
END IF;
IF currentP= endP THEN
SET finished= 1;
END IF;
END;
END IF;
UNTIL done
END REPEAT;
CLOSE cur2;
END
My issue is that the cursor isn't working properly. When I fetch the current row into pt_from, pt_to, and pt_dist all I get are NULL values. The sampledata table is properly stored in the database and all the point ids in pids are also in the sampledata table. Plus this EXACT code works for another procedure, but reusing it here isn't working.
Anybody know what I'm doing wrong?
The error was that I passed in the point ids like this '12, 15, 18' with spaces in between. MySQL counts the whitespace when it parses the strings, and the id's in the table were listed without spaces. The correct way to pass in the string set is '12,15,18'.