Missing END - Mysql Stored Procedure - mysql

I am very used to writing in TSQL and MySQL is taking some adjusting but I think I am getting most of it. I have one stored procedure however that will not budge, I have read and re-read it 100 times, read up all I can find on the error and still no luck.
On Line 11 and the end of this statement:
CREATE TEMPORARY TABLE ReturnAvalRooms (roomID int);
I get the error "Missing End" even though this is at the beginning of my query.
I have checked and double checked that everything is Declared and Set with appropriate ; but just can't figure out why I am getting this error.
Below is a full copy of the Stored Procedure.
CREATE PROCEDURE `Get_AvailRooms` (IN StartDate datetime, IN EndDate datetime, IN RoomType int)
BEGIN
DECLARE PStartDate datetime;
DECLARE PEndDate datetime;
DECLARE PRoomType int;
SET PStartDate = StartDate;
SET PEndDate = EndDate;
SET PRoomType = RoomType;
CREATE TEMPORARY TABLE AvaliableNowRooms AS (SELECT idRooms FROM rooms WHERE RoomNextAvail < PStartDate AND RoomTypeID = PRoomType);
CREATE TEMPORARY TABLE ReturnAvalRooms (roomID int);
DECLARE AvailRooms CURSOR FOR SELECT * FROM AvaliableNowRooms;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE RoomID INT;
OPEN AvailRooms;
read_loop: LOOP
FETCH AvailRooms INTO RoomID;
IF done THEN
LEAVE read_loop;
END IF;
DECLARE Bookings CURSOR FOR SELECT BookingStartDate, BookingEndDate, BookingRoomID FROM Booking WHERE BookingRoomID = RoomID AND BookingEndDate < PStartDate;
DECLARE PBookingStartDate datetime;
DECLARE PBookingEndDate datetime;
DECLARE PBookingRoomID,
OPEN Bookings
Booking_Read: LOOP
FETCH Bookings INTO PBookingStartDate, PBookingEndDate, PBookingRoomID;
IF done THEN
LEAVE Booking_Read;
END IF;
IF PBookingStartDate BETWEEN PStartDate AND PEndDate
LEAVE Booking_Read;
ELSE IF PBookingEndDate BETWEEN PStartDate AND PEndDate
LEAVE Booking_Read;
ELSE
INSERT INTO ReturnAvalRooms (id) values (PBookingRoomid);
END LOOP;
CLOSE Bookings;
END
END LOOP;
CLOSE AvailRooms;
END
SELECT * FROM ReturnAvalRooms
END

MySQL is particular about the order of the DECLARE statements. Within a block, those have to appear first. And (I believe) the HANDLERs have to be the last declarations.
I'm confused by what your procedure is intending to return. It seems like you'd want to return all rooms of the specified type for which there isn't any booking that overlaps the specified period.
But looking at the logic in the procedure, it seems like if there are no rows in booking for a given room, that room won't be returned. And that seems odd. And that has me puzzled about what this is really supposed to return. The specification is obfuscated by a lot of unnecessary clutter.
The whole rigmarole could be accomplished by a single SQL statement which is much simpler, much clearer, and more efficient to boot.
DELIMITER $$
CREATE PROCEDURE `Get_AvailRooms` (IN PStartDate datetime, IN PEndDate datetime, IN PRoomType int)
BEGIN
-- rooms of the specified type which are "available" for the
-- specified period. A room is considered not available if
-- the RoomNextAvail col has a date value later than the beginning
-- of the specified period, or there are one or more bookings that
-- overlap that period. If the specified StartDate is later than
-- the specified EndDate, then no rooms are available.
--
-- This query uses an "anti-join" pattern to return only rows
-- in rooms which don't have a overlapping booking
SELECT r.idRooms
FROM rooms r
LEFT
JOIN Booking b
ON b.BookingRoomID = r.idRooms
AND b.BookingEndDate >= PStartDate
AND b.BookingStartDate <= PEndDate
WHERE b.idRooms IS NULL
AND r.RoomTypeID = PRoomType
AND r.RoomNextAvail < PStartDate
AND PStartDate < PEndDate
ORDER BY r.idRooms;
END$$
DELIMITER ;
That's just a first cut; it's not tested. We might want to change the <= and >= to be just < and >, depending on whether or not we consider a booking that ends at exactly 10AM overlaps with a period starting right at 10AM.

Declare variables at the top of the procedure. Also you are not using THEN after IF statement. Correct the code. Also, you are closing the loop before closing the IF block.
CREATE PROCEDURE `Get_AvailRooms` (IN StartDate datetime, IN EndDate datetime, IN RoomType int)
BEGIN
DECLARE PStartDate datetime;
DECLARE PEndDate datetime;
DECLARE PRoomType int;
DECLARE AvailRooms CURSOR FOR SELECT * FROM AvaliableNowRooms;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE RoomID INT;
DECLARE Bookings CURSOR FOR SELECT BookingStartDate, BookingEndDate, BookingRoomID FROM Booking WHERE BookingRoomID = RoomID AND BookingEndDate < PStartDate;
DECLARE PBookingStartDate datetime;
DECLARE PBookingEndDate datetime;
DECLARE PBookingRoomID,
SET PStartDate = StartDate;
SET PEndDate = EndDate;
SET PRoomType = RoomType;
CREATE TEMPORARY TABLE AvaliableNowRooms AS (SELECT idRooms FROM rooms WHERE RoomNextAvail < PStartDate AND RoomTypeID = PRoomType);
CREATE TEMPORARY TABLE ReturnAvalRooms (roomID int);
OPEN AvailRooms;
read_loop: LOOP
FETCH AvailRooms INTO RoomID;
IF done THEN
LEAVE read_loop;
END IF;
OPEN Bookings
Booking_Read: LOOP
FETCH Bookings INTO PBookingStartDate, PBookingEndDate, PBookingRoomID;
IF done THEN
LEAVE Booking_Read;
END IF;
IF PBookingStartDate BETWEEN PStartDate AND PEndDate THEN
LEAVE Booking_Read;
ELSE IF PBookingEndDate BETWEEN PStartDate AND PEndDate THEN
LEAVE Booking_Read;
ELSE
INSERT INTO ReturnAvalRooms (id) values (PBookingRoomid);
END;
END LOOP;
CLOSE Bookings;
END
END LOOP;
CLOSE AvailRooms;
END
SELECT * FROM ReturnAvalRooms
END

Related

Why MySQL Cursor stopped looping while reporting a success

I created a procedure as below in MySQL workbench 8.0.12 community, win10. A cursor is declared to loop through more than 16000 member's ID in a table to insert their number of orders created in different dates into another table.
Member's IDs and their dates of registration are selected here for the cursor. select distinct BAG,AAA from DICT_ALLE_AAAF where AAA>=Date_Memb_Star. Once I call it call PRO_MIDD_MEMB_AAAA_1('2017/01/01','2019/11/10',60), it only looped through 5 member's IDs and did not report any error. But if I took the while loop out of the procedure and with select Var_BAG on, the cursor loop went selecting more than 200 member's IDs until it asked me to cancel it. Can anyone tell me what is wrong in my code? Many thanks.
delimiter //
create procedure PRO_MIDD_MEMB_AAAA_1
(in Date_Memb_Star date,
in Date_Sale_End date,
in Int_Inte int)
begin
declare Int_Floo int default 0;
declare Int_Ceil int;
declare Int_MembAll int;
declare Int_Loop int default 1;
declare Int_MaxiLoop int;
declare Int_DaySpen int default 30;
declare Int_Done int;
declare Var_BAG varchar(16);
declare Date_Memb_Regi date;
declare Cur_Memb cursor for
select distinct BAG,AAA from DICT_ALLE_AAAF where AAA>=Date_Memb_Star order by BAG;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Int_Done = 1;
truncate table MIDD_MEMB_AAAA;
open Cur_Memb;
read_loop:loop
fetch Cur_Memb into Var_BAG,Date_Memb_Regi;
if Int_Done=1 then
leave read_loop;
end if;
/*select Var_BAG;*/
set Int_MaxiLoop=ceil(datediff(Date_Sale_End,Date_Memb_Regi)/Int_Inte);
set Int_Floo=0;
while Int_Loop<=Int_MaxiLoop do
set Int_Ceil=Int_Floo+Int_Inte;
insert into MIDD_MEMB_AAAA
select Var_BAG,Int_Floo,Int_Ceil,
count(distinct BAK)*(Int_DaySpen/Int_Inte) as Numb_Con_Avg
from OPER_SALE_AAAB
where BAG=Var_BAG
and timestampdiff(hour,Date_Memb_Regi,AAA)/24>Int_Floo
and timestampdiff(hour,Date_Memb_Regi,AAA)/24<=Int_Ceil
and AAB>0;
set Int_Floo=Int_Floo+Int_Inte;
set Int_Loop=Int_Loop+1;
end while;
end loop read_loop;
close Cur_Memb;
end//
delimiter ;
Little oversight: you forgot to reset your variable Int_Loop, so after the first run, it only enters the loop when Int_MaxiLoop has a new maximum value.
Reinitialize it (to 1, I assume) before the loop:
...
set Int_Floo=0;
set Int_Loop=1;
while Int_Loop<=Int_MaxiLoop do ...

How to use cursors to update some columns according to another column?

I have a table called USER. This table has 2 columns as end_date, access_date. access_date is empty right now but i want to populate it like:
if end_date exists : access_date = end_date + 1 year (anway i can make that operation) but my problem i could not construct the cursor i have not use cursor logic before.
i need something like:
DELIMITER $$
CREATE PROCEDURE user_procedure ()
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_user varchar(100) DEFAULT "";
-- declare cursor for user
DEClARE user_cursor CURSOR FOR
SELECT * FROM USER;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN user_cursor;
get_user: LOOP
FETCH user_cursor INTO v_user;
IF v_finished = 1 THEN
LEAVE get_user;
END IF;
-- operation
-- something like:
set #end_date = select from cursor
update expiry... etcs
END LOOP get_user;
CLOSE user_cursor;
END$$
DELIMITER ;
CALL user_procedure();
but the problem i do not know how to define the cursor because as you see in example:
DECLARE v_user varchar(100) DEFAULT "";
i am pretty sure it is wrong and i try to fetch it into
FETCH user_cursor INTO v_user;
So how can i properly define the cursor and fetch as a whole row and make change ?
Edit: some people did not understand and claimed i have asked same question again, ok i will edit the real code, now as below according to people's comment. This update must be applied to each individual row.
set #key = 'bla bla';
delimiter $$
create procedure select_or_insert()
begin
IF EXISTS (select USER_EXPIRY_DATE from USER) THEN
update USER set ACCESS_EXPIRY_DATE = DATE_ADD(USER_EXPIRY_DATE, INTERVAL 1 YEAR);
ELSE IF EXISTS (select USER_START_DATE from USER) THEN
SET #start_date = (select USER_START_DATE from USER);
SET #start_date_to_be_added = aes_decrypt(#start_date,#key)
update USER set ACCESS_EXPIRY_DATE = DATE_ADD(USER_EXPIRY_DATE, INTERVAL 1 YEAR);
END IF;
end $$
delimiter ;
but in here for example:
ELSE IF EXISTS (select USER_START_DATE from USER)
is returning more than 1 row.
You don't need a cursor for that, you can simply use an update statement:
UPDATE User
SET access_date = DATE_ADD(end_date, INTERVAL 1 YEAR)
WHERE end_date IS NOT NULL

Trying to add an additional value to an existing value in a column for all the rows that are retrieved using a stored procedure for

I have spent quite a long time to figure out.. so my update statement is affecting 0 rows although I know for a fact that it should affect at least affect more than a few rows as I have tried as a standalone. In place of update statement I tried select statement and it is working so does that mean that update statement is not supposed to work in stored procedure.. I kinda doubt it.. so I would like to get a second opinion.
my stored procedure code here:
DELIMITER $$
CREATE PROCEDURE updateKeywordsInRIConsole(in retailerId int )
BEGIN
declare key_words varchar(200) default null;
declare grpid bigint(20);
declare finished bool default false;
declare cur1 cursor for
select Keywords, GRPID
from RIConsole
where RetailerID = retailerId
and DateCreated > date(now()) - interval 1 year
and INSTR(Keywords, "offer_page") = false;
declare continue handler for not found set finished = 1;
declare exit handler for sqlexception
begin
show errors;
end;
declare exit handler for sqlwarning
begin
show warnings;
end;
open cur1;
start_loop: loop
fetch cur1 into key_words, grpid;
if finished = 1 then
leave start_loop;
end if;
update RIConsole set Keywords = concat(key_words, " ",
"offer_page") where GRPID = cast(grpid as signed); <-- this code not working...I called it with cast function to make sure.. and i also tried without it.
end loop start_loop;
close cur1;
END $$
DELIMITER ;
DROP PROCEDURE updateKeywordsInRIConsole;
Yes, you can do an UPDATE in a stored procedure.
If you are happy with your SELECT, you could do the while thing in a single statement. e.g.
CREATE PROCEDURE updateKeywordsInRIConsole(IN retailerId INT)
BEGIN
UPDATE RIConsole
SET Keywords = CONCAT(Keywords, " ", "offer_page")
WHERE where RetailerID = retailerId
AND DateCreated > DATE(NOW()) - INTERVAL 1 YEAR
AND INSTR(Keywords, "offer_page") = false;
END
;

Small Procedure with Cursor for autocreate rows in tables (MYSQL)

i created an event that execute one time per month. Three tables are important here, Cuota(fee), Alumno(studient) and CuotaxAlumno(fee per studient).
My objective is create a row in table Cuota(fee) one time per month and then with that fee create a payment row for every studient (in table CuotaxAlumno).
I having syntax error in te FETCH line, line 19, and i don't find the problem. i will appreciate the help.
IS WORKING NOW. CODE UPDATED 13-04-2017 Thanks!
DELIMITER $$
CREATE PROCEDURE crearCuotas()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE referenciaMonto INT;
DECLARE referenciaAlumno INT;
DECLARE referenciaCuota INT;
DECLARE fecha DATE;
DECLARE cursorAlumno CURSOR FOR SELECT idAlumno FROM alumno WHERE idEstado=1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET referenciaMonto = (SELECT idMontoCuota FROM montocuota ORDER BY idMontoCuota DESC LIMIT 1);
SET fecha = CURDATE();
INSERT INTO cuota (idMontoCuota, fecha) VALUES(referenciaMonto, fecha);
SET referenciaCuota = (SELECT idCuota FROM cuota ORDER BY idCuota DESC LIMIT 1);
OPEN cursorAlumno;
fetch_loop: LOOP
FETCH cursorAlumno INTO referenciaAlumno;
IF done THEN
LEAVE fetch_loop;
END IF;
INSERT INTO cuotaxalumno(idAlumno, idCuota, idEstado) VALUES(referenciaAlumno, referenciaCuota, 5);
END LOOP;
CLOSE cursorAlumno;
END;
DELIMITER ;
You need to add a loop label:
CREATE PROCEDURE crearCuotas()
BEGIN
/* yada */
OPEN cursorAlumno;
fetch_loop: LOOP
FETCH cursorAlumno INTO referenciaAlumno;
IF done THEN
LEAVE fetch_loop;
END IF;
INSERT INTO cuotaxalumno (idAlumno, idCuota, idEstado)
VALUES (referenciaAlumno, referenciaCuota, 5);
END LOOP;
CLOSE cursorAlumno;
END;
This is because while the label is not required for creating a loop, it is required for the leave statement.

Mysql stored procedure fetch error

What am I doing wrong?
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAllPoints`()
BEGIN
declare done1, done2, done3, done4, pointAlive BOOLEAN DEFAULT FALSE;
*lots of declaring*
...
*lots of declaring*
declare cur1 cursor for select id, Owner_idOwner, longitude, lat,x,y from Point;
declare continue handler for not found set done1= TRUE;
DROP TEMPORARY TABLE IF EXISTS TEMP;
CREATE temporary TABLE IF NOT EXISTS TEMP (
*lots of stuff*
)ENGINE=MEMORY;
open cur1;
mainLoop: loop
fetch cur1 into pointid, ownerid, pointlong, pointlat, pointx,pointy;
if done1 then
set done1 = FALSE;
close cur1;
leave mainLoop;
end if;
insert into TEMP (тут работает);
BLOCK2:BEGIN
** Black is working right with cursor #1***
END BLOCK2 ;
BLOCK3:BEGIN
** Black is working right with cursor #2***
END BLOCK3;
That's what's not working:
pointid declared int with value "18045" fetched from cur1 cursor
call isAlive(pointid,#Alive,#timeTill);
update TEMP set Open = #Alive, TimeTill= #timeTill where Point_id = pointid;
end loop mainLoop;
select * from TEMP;
After calling isAlive loop seems to be interrupted and select query returns only 1 row with correct value in Open and TimeTill column instead of ~1200rows with correct values when isAlive is not called
END
This is the isAlive stored procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `isAlive`(IN iPoint_id int, INOUT Alive int, INOUT timeTill time)
BEGIN
declare pointAlive int;
select count(id) into pointAlive from Schedule where
day = dayofweek(curdate())-1
and Point_id = iPoint_id
and Start < curtime()
and Stop > curtime();
if poinTalive then
select pointAlive,timediff(Stop,curtime()) INTO Alive, timeTill
from Points.Schedule
where day = dayofweek(curdate()) -1
and Point_id = iPoint_id;
else
select pointAlive,timediff(str_to_date(concat(curdate(),' ',#starttime), "%Y-%m-%d %T"),now()) INTO Alive, timeTill
from Points.Schedule
where day = dayofweek(curdate()) -1
and Point_id = iPoint_id;
end if;
END
Can you tell me what am I doing wrong? How can I call isAlive for every pointid ? Thank you