How to find problems in MySQL stored procedure? - mysql

I have the following stored procedure:
DELIMITER $$
CREATE DEFINER=`195414_py82740`#`%` PROCEDURE `getUserMail`(inUserId INT)
BEGIN
DECLARE done INT default 0;
DECLARE tmpMailId INT default -1;
DECLARE tmpFromUserId INT default -1;
DECLARE cursor1 cursor for select id from mail WHERE toUserId=inUserId OR fromUserId=inUserId ORDER BY sentDate desc;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS temp_userMail;
CREATE TEMPORARY TABLE temp_userMail LIKE mail;
OPEN cursor1;
REPEAT
FETCH cursor1 INTO tmpMailId;
SET tmpFromUserId = IF ((SELECT fromUserId FROM mail WHERE id=tmpMailId)=inUserId, (SELECT toUserId FROM mail WHERE id=tmpMailId), (SELECT fromUserId FROM mail WHERE id=tmpMailId));
IF EXISTS(SELECT id FROM temp_userMail WHERE fromUserId=tmpFromUserId || toUserId=tmpFromUserId) THEN
SET tmpMailId = 0;
ELSE
INSERT INTO temp_userMail
(`id`,
`fromUserId`,
`toUserId`,
`sentDate`,
`readDate`,
`message`,
`fromUserNickName`,
`toUserNickName`,
`subject`,
`fromUserDeleted`,
`toUserDeleted`)
SELECT
(m1.id,
m1.fromUserId,
m1.toUserId,
m1.sentDate,
m1.readDate,
m1.message,
m1.fromUserNickName,
m1.toUserNickName,
m1.subject,
m1.fromUserDeleted,
m1.toUserDeleted)
FROM mail m1
WHERE m1.id=tmpMailId;
END IF;
UNTIL(done = 1)
END REPEAT;
CLOSE cursor1;
SELECT * FROM temp_userMail ORDER BY sentDate DESC;
END
The SP is saved successfully but when running it I get the cryptic exception
Error Code: 1241: Operand should contain 1 column(s).
I know what the error means but there is no line nr so I have no idea where to look for the problem?

Remove brackets from Your SELECT subquery
dont use
SELECT (id, ... m1.toUserDeleted) ...
use
SELECT id, ... m1.toUserDeleted ...

Related

declaring variable inside mysql stored procedure

we are trying to declare a variable inside mysql stored procedure that has transaction implemented in it. but it seems to be giving a syntax error :
following is the syntax of the stored procedure:
CREATE PROCEDURE `sp_MarkAppointmentRefferal`(
p_AppId bigint,
p_NewLocation bigint,
p_userId bigint,
p_ReferralReason varchar(500),
p_NewLocationName varchar(100)
)
begin
declare v_OldLocation int default 0;
set v_OldLocation = (select LocationId FROM appointments where iAppID = p_AppId limit 1 );
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
select -1;
END;
START TRANSACTION;
update table
set is_referred = 1,
referred_timestamp = now(),
referral_reason = p_ReferralReason
where iAppID = p_AppId
limit 1;
-- create a new appointment for the new referred location..
insert into appointments
(vAppName, vAppType, dAppDate, vCell, iPatID, iAppStatus, iuserid, iActive,
dInsertDate, iHSID, daily_ticket_no, LocationId, visit_id, encounter_id, ReferredFrom,ReferredOPDName, opd_name )
select vAppName, vAppType, now(), vCell, iPatID, iAppStatus, p_userId,
1, now(), iHSID, fn_GenerateNextAppointmentTicket(now(),p_NewLocation) , p_NewLocation, visit_id, encounter_id+1,
(select LocationId FROM appointments where iAppID = p_AppId limit 1),
(select OPD_Name FROM appointments where iAppID = p_AppId limit 1), p_NewLocationName
FROM appointments
where iAppID = p_AppId limit 1;
select LAST_INSERT_ID();
COMMIT;
end;
the syntax checker is saying that declare command is not valid here.
have also tried to place this inside the transaction clause and similar error shows up ..
any help is appreciated..
All declare statements should be at the top of the stored procedure body. Moving DECLARE EXIT HANDLER before the SET statement should fix the problem.

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.

Temporary table definition in MySQL

I have a stored procedure which uses temporary tables so that I can summarize the sales of all the products within a certain product category. When I tried to run the code it failed. I search on google and here on stackoverflow but couldn't find what I had done wrong. I'm using MySQL server 5.5 on Windows Server.
CREATE PROCEDURE `getStatistics`(IN `startDate` date,IN `endDate` date,IN `categoryName` varchar)
BEGIN
CREATE TEMPORARY TABLE procResult(productName VARCHAR, amount INT);
CREATE TEMPORARY TABLE tblProductID(SELECT ID, `name` FROM product WHERE categoryID = (SELECT ID FROM category WHERE `name` = categoryName));
DECLARE done_amt, done_PID INT DEFAULT FALSE;
DECLARE amount, productID INT DEFAULT 0;
DECLARE pidCursor CURSOR FOR SELECT ID, `name` FROM tblProductID;
DECLARE amtCursor CURSOR FOR SELECT orderlines.amount FROM orderlines WHERE orderlines.productID = productID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_amt = TRUE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_PID = TRUE;
OPEN pidCursor;
pid_loop:LOOP
DECLARE productName VARCHAR;
FETCH pidCursor INTO productID, productName;
IF done_PID THEN
LEAVE pid_LOOP;
END IF;
OPEN amtCursor;
amt_loop:LOOP
DECLARE tmpAmount INT DEFAULT 0;
FETCH amtCursor INTO tmpAmount;
IF done_amt THEN
LEAVE amt_loop;
END IF;
amount = amount + tmpAmount;
END LOOP;
CLOSE amtCursor;
IF amount > 0 THEN
INSERT INTO procResult VALUES (productName, amount);
amount = 0;
END IF;
END LOOP;
CLOSE pidCursor;
END;
You must define the length of VARCHAR type variables, such as the categoryName parameter to your stored procedure;
You must DECLARE all local variables at the very start of a BEGIN ... END compound statement block, before any other commands;
Your syntax for CREATE TABLE ... SELECT is incorrect;
You have declared two handlers for the same SQL condition, only one of which will be executed (indeterminately);
You will need to change your client's statement delimiter in order for it to understand that the semicolons appearing within the procedure body do not terminate the CREATE PROCEDURE statement;
Your entire procedure is an extremely complicated way of doing a fairly simple task in SQL:
CREATE TEMPORARY TABLE procResult
SELECT product.name, SUM(orderlines.amount) AS amount
FROM orderlines
JOIN product ON product.ID = orderlines.productID
JOIN category ON category.ID = product.categoryID
WHERE category.name = ?
GROUP BY product.ID
HAVING amount > 0

MySQL INSERT WHERE a variable is not duplicated

I have some troubles with DECLARE, CALL function and a IF settelment. What i have so far is:
CREATE PROCEDURE number_of_projects(project_name VARCHAR)
BEGIN
DECLARE variable INT;
SET variable = 4;
SELECT variable;
SELECT count(project_id) FROM atm_projects WHERE project_name = variable;
END
IF number_of_projects("PROJECT NAME") = 0
THEN
INSERT INTO atm_projects(project_id,project_name,added_from_mti)
VALUES (project_id,'PROJECT NAME',1)
ENDIF
The main goal is to insert a row into a table where project_name is not duplicated.
I could change project_name to an UNIQUE key but please tell me what is wrong with my code, an how can i fix this?
I need to learn how a PROCEDURE, CALL procedure, IF works.
try:
CREATE PROCEDURE number_of_projects(project_name VARCHAR(255))
BEGIN
DECLARE var_project_no INT;
SET var_project_no = 0;
SELECT var_project_no;
SELECT count(project_id) INTO var_project_no FROM atm_projects WHERE var_project_name = 4;
IF var_project_no = 0
THEN
INSERT INTO atm_projects(project_id,project_name,added_from_mti)
VALUES (project_id,'PROJECT NAME',1)
END IF;
END;
You have declared project_name to varchar without specifying the length. Change it to varchar(100)
CREATE PROCEDURE number_of_projects(project_name VARCHAR(255))
BEGIN
if NOT exists(SELECT Top 1 1 FROM atm_projects WHERE var_project_name = 4)
BEGIN
INSERT INTO atm_projects(project_id,project_name,added_from_mti)
Select project_id,project_name ,1
END
END;

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.
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;
You need to declare all of the cursors up front, before the logic of your procedure begins.
If you do things in this order it should work:
DECLARE variables
DECLARE cursors
DECLARE handlers
...
logic