Search code examples
mysqlstored-proceduresloopscursor

Having trouble doing multiple cursors in a MySQL stored procedure


I'm writing a store procedure to create two temporary tables do an union select of the two. When using either first or second cursor alone with the other commented the procedure works, but when I run the query to create the procedure with the 2 cursors, it fails.i've changed the code to reflect Ike Walker's suggestion.

Here is the script:

DELIMITER //

DROP PROCEDURE IF EXISTS joinemailsmsdailygraph//

CREATE PROCEDURE joinemailsmsdailygraph(IN previousDay VARCHAR(20), IN today VARCHAR(20))
READS SQL DATA

BEGIN

DECLARE hours INT;
DECLARE sms INT;
DECLARE email INT;
DECLARE smsdone INT DEFAULT 0;
DECLARE emaildone INT DEFAULT 0;


DECLARE cursorsms CURSOR FOR SELECT HOUR(sm.date_created) AS `HOUR OF DAY`, COUNT(*) AS smscount 
FROM sms_message_delivery smd 
JOIN sms_message sm ON sm.sms_message_id = smd.sms_message_id
WHERE DATE(sm.date_created) >= DATE(previousDay) AND DATE(sm.date_created) < DATE(today) 
GROUP BY HOUR(sm.date_created);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET smsdone =1;


DECLARE cursoremail CURSOR FOR SELECT HOUR(em.date_created) AS `HOUR OF DAY`, COUNT(*) AS emailcount 
FROM email_message_delivery emd
LEFT JOIN email_message em ON emd.email_message_id=em.email_message_id
WHERE DATE(em.date_created) >= DATE(previousDay) AND DATE(em.date_created) < DATE(today)  
GROUP BY HOUR(em.date_created);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET emaildone =1;

DROP TEMPORARY TABLE IF EXISTS tempsms;
CREATE TEMPORARY TABLE tempsms (hours_day INT, sms_count INT, email_count INT);

OPEN cursorsms;
sms_loop: LOOP

FETCH cursorsms INTO hours , sms;

IF smsdone = 1 THEN
 LEAVE sms_loop;
END IF;

INSERT INTO tempsms (hours_day, sms_count) VALUES (hours, sms);

END LOOP sms_loop;
CLOSE cursorsms;


DROP TEMPORARY TABLE IF EXISTS tempemail;

CREATE TEMPORARY TABLE tempemail (hours_day INT , sms_count INT , email_count INT);

OPEN cursoremail;
email_loop: LOOP

FETCH cursoremail INTO hours, email;

IF emaildone=1 THEN
 LEAVE email_loop;
END IF;

INSERT INTO tempemail(hours_day, email_count) VALUES(hours, email);

END LOOP email_loop;
CLOSE cursoremail;


SELECT hours_day, sms_count , email_count FROM tempsms
UNION
SELECT hours_day, sms_count, email_count FROM tempemail;

END//
DELIMITER;

it gives this as error

Query : CREATE PROCEDURE joinemailsmsdailygraph(IN previousDay VARCHAR(20), IN today VARCHAR(20)) READS SQL DATA BEGIN DECLARE hours INT...
Error Code : 1338
Cursor declaration after handler declaration
Execution Time : 00:00:00:000
Transfer Time : 00:00:00:000
Total Time : 00:00:00:000

ive tried putting both continue handlers at the end of all declare section but it complains about declare block overlapping or so.

Can you please tell me what I'm doing wrong? Thanks for reading.


Solution

  • Why are you using cursors ? You could easily do this without a tmp table also by just using a union.

    drop procedure if exists join_email_sms_daily_graph;
    
    delimiter #
    
    create procedure join_email_sms_daily_graph
    (
    in previousDay varchar(20), 
    in today varchar(20)
    )
    begin
    
    create temporary table tmp
    (
     hours_day int unsigned, 
     sms_count int unsigned default 0, 
     email_count int unsigned default 0
    )engine=memory;
    
    insert into tmp (hours_day, sms_count) 
    select
     hour(sm.date_created) as hours_day, 
     count(*) AS sms_count 
    from
     sms_message_delivery smd 
    join sms_message sm ON sm.sms_message_id = smd.sms_message_id
    where
     date(sm.date_created) >= date(previousDay) and date(sm.date_created) <  date(today) 
    group by
     hour(sm.date_created);
    
    insert into tmp (hours_day, email_count) 
    select 
     hour(em.date_created) as hours_day, 
     count(*) AS email_count 
    from
     email_message_delivery emd
    left join email_message em ON emd.email_message_id=em.email_message_id
    where
     date(em.date_created) >= date(previousDay) and date(em.date_created) < date(today)  
    group by
     hour(em.date_created);
    
    select * from tmp; 
    
    drop temporary table if exists tmp;
    
    end#
    
    delimiter;