mysql. Stored Procedure Error 1328 - mysql

This is really baffling me. I am converting a simple procedure from informix into mysql. What it basically does is tell me what the next event is from an event table and a calendar table. In informix the procedure is simple.
FOREACH
SELECT date,weekno,event
INTO l_date,l_week,l_event
FROM event,calendar
WHERE dayno = dayno
AND date = l_today
AND start >= l_now
UNION
SELECT date,weekno,event
FROM event,calendar
WHERE dayno = dayno
AND date > l_today
UNION
SELECT TODAY,9999,9999
FROM event,calendar
WHERE dayno = dayno
AND event = (SELECT MAX(event) FROM event)
ORDER BY 3
if l_event = 9999 then <error> end if;
EXIT FOREACH
END FOREACH
So basically the query finds the next event and returns it. l_today and l_event are parameters that are passed. So on to the mysql version.
looper: BEGIN
DECLARE curs1 CURSOR FOR
SELECT CONCAT("SELECT date, weekno, event FROM event INNER JOIN calendar ON dayno = dayno",
" WHERE date = '", lv_today ,"' AND start >= '", lv_time ,"'",
" UNION SELECT date, weekno, event FROM event INNER JOIN calendar ON dayno = dayno WHERE date > '", lv_today ,"'",
" UNION SELECT DATE(NOW()) AS date, 9999 AS weekno, 9999 AS event FROM event INNER JOIN calendar ON dayno = dayno",
" WHERE (SELECT MAX(event) FROM event) ORDER BY event ");
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN curs1;
loop1: LOOP
FETCH curs1 INTO ldate, lweek, levent;
SELECT ldate, lweek, levent;
LEAVE looper;
END LOOP loop1;
END;
I haven't checked that the rest of the methodology works because I get this error:
Incorrect number of FETCH variables.
Does this mean that I have declare a different variable for each of the query returns? I am new to mysql. If this is the case what would be the best way to solve this conundrum? I have also changed to column and table names.
Many thanks

looper: BEGIN
DECLARE curs1 CURSOR FOR
SELECT eve_date, dia_weekno, eve_event
FROM game_event
INNER JOIN stan_calendar ON eve_abs_dayno = dia_abs_dayno
WHERE eve_date = lv_today
AND eve_start >= lv_time
UNION
SELECT eve_date, dia_weekno, eve_event
FROM game_event
INNER JOIN stan_calendar ON eve_abs_dayno = dia_abs_dayno
WHERE eve_date > lv_today
UNION SELECT DATE(NOW()) AS eve_date, 9999 AS dia_weekno, 9999 AS eve_event
FROM game_event
INNER JOIN stan_calendar ON eve_abs_dayno = dia_abs_dayno
WHERE (SELECT MAX(eve_event) FROM game_event) ORDER BY eve_event;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN curs1;
curs_loop: LOOP
FETCH curs1 INTO lv_date, lv_week, lv_event;
SELECT lv_date, lv_week, lv_event;
LEAVE looper;
CLOSE curs1;
END LOOP curs_loop;
Thank you for answering my question, I have put it back to how I thought it was... and it now works. Here is the loop in full.

https://dev.mysql.com/doc/refman/5.7/en/fetch.html The number of columns retrieved by the SELECT statement must match the number of output variables specified in the FETCH statement so yes but in fact you are only selecting 1 concatenated string - I think your first problem is with the select syntax which doesn't need the concat, the brackets or the quotes (unless you are trying to create a prepared statement for some reason - and even if you were I doubt if the code would be correct)
Simple Cursor
DROP PROCEDURE IF EXISTS EC;
DELIMITER $$
CREATE PROCEDURE `EC`(
IN `inemp_no` varchar(255)
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
LOOPER:begin
DECLARE done INT DEFAULT FALSE;
declare ename varchar(20);
declare esalary int default 0;
declare emp_cursor CURSOR FOR
SELECT last_name,salary FROM employees where emp_no= inemp_no ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open emp_cursor;
read_loop: loop
fetch emp_cursor into ename,esalary;
if done then leave read_loop; end if;
insert into debug_table (msg) values(concat('employee:',ename,' earns:',esalary));
end loop;
close emp_cursor;
end $$
DELIMITER ;
MariaDB [sandbox]> truncate table debug_table;
Query OK, 0 rows affected (0.22 sec)
MariaDB [sandbox]> call ec(2);
Query OK, 0 rows affected (0.03 sec)
MariaDB [sandbox]> select * from debug_table;
+----+--------------------------+------+
| id | msg | MSG2 |
+----+--------------------------+------+
| 1 | employee:BBB earns:39500 | NULL |
+----+--------------------------+------+
1 row in set (0.00 sec)

Related

Why stored procedure takes more time to execute compared to stored function for same code?

I have created stored-procedure with OUT parameter to handle automate INSERT operations based on values of other tables. I also made stored-function for the same operation. I am facing a problem with execution time for both is heavily different which makes me worried and I don't know why is that.
Following table values in summary:
2016 rows in emp_personal
I am using MariaDB 10.3.14. I am testing with HeidiSQL 10.1.0.5577.
I also tried calling it from Node.JS using MariaDB Node.js Connector. But in both cases, I had almost the same result.
This is Stored Procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_auto_attendance`(
OUT `result` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';
-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE();
OPEN empIds_cursor;
get_id: LOOP
SET attendance = 'Present';
FETCH empIds_cursor INTO empId;
-- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
IF done = 1 THEN
LEAVE get_id;
END IF;
-- Check if public holiday for employee
IF isHoliday > 0 THEN
SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd
LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId
LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
IF isEmpHoliday > 0 THEN
SET attendance = 'Public Holiday';
END IF;
END IF;
-- if not public holiday, Check if week off
IF attendance <> 'Public Holiday' THEN
SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0;
IF isWeekOff > 0 THEN
SET attendance = 'Week Off';
END IF;
END IF;
-- if not week off, Check if approved leave
IF attendance <> 'Week Off' THEN
SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
IF isApprovedLeave > 0 THEN
SET attendance = 'Approved Leave';
END IF;
END IF;
-- insert attendance
INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
SET insertedRows = insertedRows + 1;
END LOOP get_id;
CLOSE empIds_cursor;
SET result = insertedRows;
END
This is Stored Function
CREATE DEFINER=`root`#`localhost` FUNCTION `test_auto_att_func`()
RETURNS int(11)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';
-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE();
OPEN empIds_cursor;
get_id: LOOP
SET attendance = 'Present';
FETCH empIds_cursor INTO empId;
-- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
IF done = 1 THEN
LEAVE get_id;
END IF;
-- Check if public holiday for employee
IF isHoliday > 0 THEN
SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd
LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId
LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
IF isEmpHoliday > 0 THEN
SET attendance = 'Public Holiday';
END IF;
END IF;
-- if not public holiday, Check if week off
IF attendance <> 'Public Holiday' THEN
SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0;
IF isWeekOff > 0 THEN
SET attendance = 'Week Off';
END IF;
END IF;
-- if not week off, Check if approved leave
IF attendance <> 'Week Off' THEN
SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
IF isApprovedLeave > 0 THEN
SET attendance = 'Approved Leave';
END IF;
END IF;
-- insert attendance
INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
SET insertedRows = insertedRows + 1;
END LOOP get_id;
CLOSE empIds_cursor;
RETURN insertedRows;
END
Time to execute Stored Function
SELECT `test_auto_att_func`();
/* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 0.328 sec. */
Time to execute Stored Procedure
CALL `sp_auto_attendance`(#res);
SELECT #res;
/* Affected rows: 6,049 Found rows: 1 Warnings: 0 Duration for 2 queries: 00:01:21.6 */
Stored Function Output and Stored Procedure Output
Can anyone please explain why this is happening? And if I am doing anything wrong, please let me know how should I correct it?
Thank You.
There should be no difference on performance on similar code when you run the code as a procedure vs as a function.
Most likely explanation (see Affected rows) is that at the the time of the function run, the cursor did not find any active persons whereas in the procedure run did, hence running the loop queries.

error in stored procedure ...into keyword

Two tables Borrower(rollno,name,bookissue_date) and Fine(rollno,name,amount)
delimiter //
create procedure student( in roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
Declare cur cursor for
select Dateofiss from Borrower where Roll_no = roll into Dateofiss1;
OPEN cur;
fetch cur into Dateofiss1
if(datediff(sysdate(),Dateofiss1)<15) then varchar(20))
update Borrower set status='R'where Roll_no=roll_no
elseif(datediff(sysdate(),Dateofiss1)>=15)and datediff (sysdate(),Dateofiss1<30)
SET FINEAMOUNT=5*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
elseif (datediff(sysdate(),Dateofiss1)>30)
SET FINEAMOUNT=50*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
close cur;
end if
select * from Borrower;
elect * from Fine;
end
You have a number of syntax errors.
You have an extraneous varchar(20)) in the first if statement.
You're missing THEN in the ELSEIF statements.
You wrote update.borrower instead of update borrower.
You have roll_no in quotes in some of your update statements.
The roll_no parameter is the same as a table column, since column names are case-insensitive. The condition where Roll_no = roll_no will match every row because of this. Give the parameter a different name.
In a SELECT, the INTO clause goes after FROM, not at the end.
There's no need to use a cursor if you're using SELECT INTO. Just execute the query and it will set the variable.
You can also simplify the code by putting the date difference in a variable, so you don't have to repeatedly calculate it. And in the ELSEIF you don't need to test >= 15, since you'll only get there if the < 15 test failed.
The UPDATE statement is the same in all conditions, so it doesn't need to be in the IF at all.
delimiter //
create procedure student( in p_roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
declare diff INT;
select Dateofiss from Borrower into Dateofiss1 where Roll_no = p_roll_no;
OPEN cur;
SET diff = datediff(sysdate(),Dateofiss1)
IF diff BETWEEN 15 AND 29 THEN
SET FINEAMOUNT= 5 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
else
SET FINEAMOUNT= 50 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
end if
update Borrower set status='R'where Roll_no=p_roll_no
select * from Borrower;
select * from Fine;
end

Convert SQL Server Cursor To MySQL

How to convert a SQL Server cursor to MySQL?
ALTER function [dbo].[Terlambat](#proses int) returns #tempKK table (krk int,terlambat int) as
begin
declare #uid int, #total_hari_kerja int
select #total_hari_kerja = SUM(alokasi) from proses
declare krk_uid cursor for select distinct krk from kartu_kendali where proses <= 8
open krk_uid
fetch next from krk_uid into #uid
while (##FETCH_STATUS = 0)
begin
insert into #tempKK select top 1 krk, dbo.GetTerlambat(realisasi_tgl_terima, #total_hari_kerja) terlambat from kartu_kendali where proses = 1 and krk = #uid order by RecID
fetch next from krk_uid into #uid
end
close krk_uid
deallocate krk_uid
return
end
For changing a cursor to MySQL that has no cursor you should follow this solution:
Add a Row Number to your SELECT statement that help you to fetch data from your SELECT over it.
Now, you can use a variable that starts with 1 and grows inside the WHILE in each step by 1 and filter your SELECT statement over Row Number and this variable.

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.

MYSQL Stored Procedure Issues

I am writing a MySQL Stored Procedure for the first time, and I am running into an issue - I think with the Handler Code. Basically, I want this code to update all rows in the pps_users table, but for some reason I am hitting the 'finished condition' for the handler after only two rows are fetched.
I tried the same thing with the REPEAT syntax and got the same result. If I just run the cursor query I correctly get the 10,000 records I expect, but when I run the whole thing as is, I hit the finished code after only 1 or 2 records.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `changeNFLFavTeams`()
BEGIN
DECLARE favNFLTeam varchar(100) DEFAULT "";
DECLARE favNCAATeam varchar(100) DEFAULT "";
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE user_id bigint(20);
DECLARE fullNameOfTeam varchar(100) DEFAULT "";
DECLARE update_favs CURSOR FOR select id, favorite_nfl_team from pps_users WHERE favorite_nfl_team is not null;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
OPEN update_favs;
updaterecord: LOOP
FETCH update_favs INTO user_id, favNFLTeam;
select user_id, favNFLTeam as "Test";
if v_finished = 1
then
select "finished" as "finished";
LEAVE updaterecord;
end if;
select full_name into fullNameOfTeam
from teams t
inner join display_names dt on dt.entity_id = t.id
and dt.entity_type = 'teams'
and dt.first_name = favNFLTeam
and team_key like 'l.nfl.com%' LIMIT 1;
select user_id, fullNameOfTeam AS "BeforeUpdate";
IF fullNameOfTeam != ''
THEN
-- here for whatever_transformation_may_be_desired
-- Find the Full name for the record they chose
UPDATE pps_users p
SET favorite_nfl_team = fullNameOfTeam
WHERE user_id = p.id;
ELSE
SELECT 'A' AS 'A'; -- no op
END IF;
end loop updaterecord;
CLOSE update_favs;
END
This is because if your SELECT full_name into fullNameOfTeam... query returns no rows, then it will set v_finished to 1. That, apparently, happens early on, and forces an exit from the main loop.
The key is to realize that the CONTINUE HANDLER for NOT FOUND does not apply to the cursor alone.
You should either put the secondary query into its own BEGIN..END block with its own CONTINUE handler, or (easier) just set v_finished = 0 after the SELECT full_name into fullNameOfTeam... statement.