I have a stored procedure like the following. There is an error showing at the update statement before the cursor declaration. When I remove the cursor statement, the stored procedure works fine. Why can't be there an update statement before the cursor statement?
DROP PROCEDURE IF EXISTS GenerateAlert;
DELIMITER $$
CREATE PROCEDURE GenerateAlert()
BEGIN
DECLARE done INTEGER DEFAULT FALSE;
DECLARE temp_project_id INT DEFAULT 0;
DECLARE temp_finish_time DATETIME DEFAULT NOW();
DECLARE _message_class INT DEFAULT 3;
DECLARE proj_user_id INT DEFAULT 0;
-- get message text template
DECLARE message_template VARCHAR(255) DEFAULT
(
SELECT alert_message_template FROM alerts WHERE id = 6
);
-- get maximum allowed minutes
DECLARE allowed_time INT DEFAULT
(
SELECT alert_value_1 FROM alerts WHERE id = 4
);
-- flag all messages with class 3 to deleted = true
UPDATE messages
SET is_deleted = 1
WHERE messages_class = 3;
DECLARE _cur CURSOR FOR
SELECT d.project_id, MAX(finish_time)
FROM
client_docs AS d INNER JOIN
issues AS i ON d.project_id = i.project_id INNER JOIN
working_times AS t ON i.id = t.issue_id
WHERE
d.status = 1 AND
t.finish_time IS NOT NULL AND
t.category = 1
GROUP BY d.project_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN _cur;
read_loop: LOOP
FETCH _cur INTO temp_project_id, temp_finish_time;
IF done THEN
LEAVE read_loop;
END IF;
-- add more minutes to finish time
SET temp_finish_time = DATE_ADD(temp_finish_time, INTERVAL allowed_time MINUTE);
IF temp_finish_time < NOW() THEN
-- this project must be alerted
-- get project user
SELECT c.staff INTO proj_user_id
FROM
projects AS p INNER JOIN
clients AS c ON p.client_id = c.id
WHERE
p.id = temp_project_id;
END IF;
END LOOP read_loop;
CLOSE _cur;
END
$$
DELIMITER ;
Found the answer: The declaration statments must come before any other transactional statement in MySQL stored procedure.
SQL, missing end, but why? Second comment in the answer.
Related
Hello I have the following stored procedure for MySQL but when it is executed in my ASP.NET Core application I get a Subquery returns more than 1 row error. What am I doing wrong here? The equivalent SQL Server version used to work without problems...
-- System Calculates Candidate’s Matching Score based on a Manager’s Answer Weights
CREATE PROCEDURE spSysCalcCandScore
(
IN Candidate_ID INT,
IN Manager_ID INT
)
Begin
DECLARE ansID INT;
DECLARE tempSum INT;
DECLARE Sum INT;
DECLARE Done INT DEFAULT FALSE;
DECLARE MyCursor CURSOR FOR
SELECT Answer_ID FROM Completed_Questionnaire
WHERE Candidate_ID = Candidate_ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Done = TRUE;
START TRANSACTION;
OPEN MyCursor;
myloop: LOOP
FETCH MyCursor INTO ansID;
IF Done THEN
LEAVE myloop;
END IF;
SET tempSum = (SELECT Weight_Value FROM Weight WHERE (Answer_ID = ansID AND Manager_ID = Manager_ID));
SET Sum = Sum + tempSum;
END LOOP;
CLOSE MyCursor;
IF (Sum IS NULL) THEN
SET Sum = 0;
END IF;
UPDATE `Interest`
SET Matching_Score = Sum
WHERE (Candidate_ID = Candidate_ID AND Manager_ID = Manager_ID);
COMMIT;
End//
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
I appreciate your answer. I changed the procedure with your changes, but I've done something wrong. Would you please tell me what is wrong with the code?
So I'll need to include the code at the end of the main question.
Attempt #2 is at the bottom.
Can someone tell me what is wrong with this procedure? The system tells me there is an error at:
DECLARE myemail_cursor CURSOR FOR
USE v_db; SELECT DISTINCT u.mail FROM users u INNER JOIN users_roles ur ON u.uid=ur.uid INNER JOIN role r ON ur.rid=r.rid WHERE r.name = 'court administrator' AND from_unixtime(u.access) >= NOW() - INTERVAL 1 YEAR;
If I run USE mydb; SELECT...in a query it works fine. Can I not use 'USE variable; select...' in a procedure?
BEGIN
DECLARE v_finished, v_finished1 BOOLEAN DEFAULT FALSE;
DECLARE v_email varchar(4000) DEFAULT "";
DECLARE v_db varchar(400) DEFAULT "";
DECLARE mydb_cursor CURSOR FOR
SELECT TABLE_SCHEMA AS 'database' FROM information_schema.TABLES WHERE TABLE_TYPE = 'BASE TABLE' and table_name='users';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = TRUE;
open mydb_cursor;
get_mydb: LOOP
FETCH FROM mydb_cursor INTO v_db;
IF v_finished THEN
CLOSE mydb_cursor;
LEAVE get_mydb;
END IF;
DECLARE myemail_cursor CURSOR FOR
USE v_db; SELECT DISTINCT u.mail FROM users u INNER JOIN users_roles ur ON u.uid=ur.uid INNER JOIN role r ON ur.rid=r.rid WHERE r.name = 'court administrator' AND from_unixtime(u.access) >= NOW() - INTERVAL 1 YEAR;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished1 = TRUE;
OPEN myemail_cursor;
get_myemail: LOOP
FETCH FROM myemail_cursor INTO v_email';
IF v_finished1 THEN
CLOSE myemail_cursor;
SET v_finished1 = FALSE;
LEAVE get_myemail;
END IF;
-- build email list
SET email_list = CONCAT(v_email," ",email_list);
END LOOP get_myemail;
CLOSE myemail_cursor;
END LOOP get_email;
CLOSE email_cursor;
END LOOP get_mydb;
END
Attempt #2.
I am getting this error: table 'v_db.users' doesn't exist
The first cursor only selects DBs where the table users exists, so I can't figure out why I"m getting the error.
BEGIN
DECLARE v_finished, v_finished1 BOOLEAN DEFAULT FALSE;
DECLARE v_email varchar(4000) DEFAULT "";
DECLARE v_db varchar(400) DEFAULT "";
DECLARE mydb_cursor CURSOR FOR
SELECT TABLE_SCHEMA AS 'database' FROM information_schema.TABLES WHERE TABLE_TYPE = 'BASE TABLE' and table_name='users';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = TRUE;
open mydb_cursor;
get_mydb: LOOP
FETCH FROM mydb_cursor INTO v_db;
IF v_finished THEN CLOSE mydb_cursor;
LEAVE get_mydb;
END IF;
BLOCK2: BEGIN
DECLARE myemail_cursor CURSOR FOR
SELECT DISTINCT u.mail FROM v_db.users u INNER JOIN v_db.users_roles ur ON u.uid=ur.uid INNER JOIN v_db.role r ON ur.rid=r.rid WHERE r.name = 'court administrator' AND from_unixtime(u.access) >= NOW() - INTERVAL 1 YEAR;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished1 = TRUE;
OPEN myemail_cursor;
get_myemail: LOOP
FETCH FROM myemail_cursor INTO v_email;
IF v_finished1 THEN
CLOSE myemail_cursor;
LEAVE get_myemail;
END IF;
SET email_list = CONCAT(v_email," ",email_list);
END LOOP get_myemail;
END BLOCK2;
END LOOP get_mydb;
END
23.2.1 Stored Routine Syntax
...
When the routine is invoked, an implicit USE db_name is performed (and undone when the routine terminates). USE statements within stored routines are not permitted.
...
Try:
...
DECLARE mydb_cursor CURSOR FOR
SELECT
DISTINCT u.mail
FROM
v_db.users u
INNER JOIN v_db.users_roles ur ON u.uid=ur.uid
INNER JOIN v_db.role r ON ur.rid=r.rid
WHERE
r.name = 'court administrator' AND
from_unixtime(u.access) >= NOW() - INTERVAL 1 YEAR;
...
Remember:
13.6.3 DECLARE Syntax
...
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other
statements.
...
So I'm trying to create a stored procedure in MySQL version 5.5.
I'm not sure what is wrong but what I want to accomplish is.
Get record From Table-A that over 7 days old. And then insert into Table-B, but I need to check if it is exist in Table B. If it is exists then skip, else Insert it.
So here is my code:
DROP PROCEDURE IF EXISTS `move_record`;
DELIMITER //
CREATE PROCEDURE `move_record`()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE dt DATETIME;
DECLARE uid,value BIGINT(20);
DECLARE category VARCHAR(30);
DECLARE data,comments VARCHAR(255);
DECLARE cancel TINYINT(1) DEFAULT NULL;
DECLARE curs CURSOR FOR SELECT `datetime`,user_id,category,data,comments,cancel FROM `record` WHERE `datetime` < DATE_SUB(CURDATE(), INTERVAL 7 DAY);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN curs;
myloop: LOOP
FETCH NEXT FROM curs INTO dt,uid,category,data,comments,cancel;
IF done THEN
LEAVE myloop;
END IF;
IF NOT EXISTS (SELECT * FROM `record_arc`
WHERE record_arc.`datetime` = dt
AND record.user_id = uid )
INSERT INTO `record_arc` (`datetime`,user_id,category,data,comments,cancel) VALUES (dt,uid,category,data,comments,cancel);
END IF;
END LOOP myloop;
CLOSE curs;
DEALLOCATE curs;
END//
DELIMITER ;
Maybe you can do this without a loop.
INSERT INTO `record_arc`(
`datetime`,
user_id,
category,
data,
comments,
cancel
)
SELECT
`datetime`,
user_id,
category,
data,
comments,
cancel
FROM `record` r
WHERE
`datetime` < DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND NOT EXISTS(
SELECT 1
FROM `record_arc` r2
WHERE
r2.`datetime` = r.`datetime`
AND r2.user_id = r.user_id
)
Alias name you used it wrongly. Check the below code
IF NOT EXISTS (SELECT 1 FROM `record_arc`
WHERE record_arc.`datetime` = dt
AND record_arc.user_id = uid )
DELIMITER //
DROP PROCEDURE IF EXISTS sp_vk_suspend//
CREATE PROCEDURE sp_vk_suspend2()
declare today = DATETIME DEFAULT NULL;
declare accid = INT DEFAULT 11;
set today = now();
BEGIN
SELECT * FROM members where expirydate < today;
END //
This is my stored procedure. I have to add one or more query depend with id from members table. How can I retrieve id from members table.
ex: I have to add this query in my sp
UPDATE members
SET suspend = 1
WHERE id = 'some id from mem: tables'
i see IMHO cursors it's what you need
DECLARE done INT DEFAULT FALSE;
DECLARE cur1 CURSOR FOR SELECT id FROM members where expirydate < today;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE a INT;
OPEN cur1;
my_loop: LOOP
FETCH cur1 INTO a
IF done THEN
LEAVE my_loop;
END IF;
UPDATE members SET suspend=1 WHERE id = a;
INSERT INTO another(member_id) values(a);
END LOOP;
CLOSE cur1;