MySQL procedure gone wrong - mysql

I have a MySQL database in which I have the following rows (by exemple) created by default (id, task and case may be different but the current value is always 1)
....idtaskcaseuser............datecurrent
238......31001.....0..............null..........1
239......41001.....0..............null..........1
I have to randomly create rows like this with insert statement (new rows). As you can see a date is filled and de current equal 0
....idtaskcaseuser............datecurrent
240......51001.....12015.04.03..........0
241......21002.....12015.04.03..........0
When I come across one of the lines created by default I want to use an update instead of an insert statement.
So I created the following procedure in MySQL
DELIMITER //
DROP PROCEDURE IF EXISTS FillProgress//
CREATE PROCEDURE FillProgress ( get_case INT(10),get_task INT(10), get_user INT(10) )
BEGIN
DECLARE test tinyint(1);
SET test = (SELECT COUNT(*) FROM progress WHERE case_id = get_case AND task_id = get_task);
IF test = 1 THEN
UPDATE progress SET current = 0, date = NOW(), user_id = get_user WHERE task_id = get_id AND case_id = get_case;
ELSE
INSERT INTO progress(task_id,case_id,user_id,date,current) VALUES (get_task,get_case,get_user,NOW(),0);
END IF;
END; //
DELIMITER ;
I use count to see if a already have a row with the same case and task. If it's true (test=1) I use UPDATE, otherwise and use INSERT.
If I test with the following row already wrote in the database
....idtaskcaseuserdatecurrent
241......41001.....0..null..........1
I use CALL FillProgress(1001,4,1);
The row is not updated, but I do not have any error message.
11:38:02 CALL FillProgress(1001,4,1) 0 row(s) affected 0.000 sec
And if I manually use my update query
UPDATE progress SET current = 0, date = NOW(), user_id = 1 WHERE task_id = 4 AND case_id = 1001;
It works like a charm.
The insert query also works fine.

The UPDATE query within the procedure has a "WHERE task_id = get_id" clause, however I don't see get_id being defined in the procedure; there is a "get_task" parameter for the stored procedure, though.

Related

MYSQL drop table when all the values within a specific column are equal to "DONE"

I am programming MYSQL and I use Python on Raspberry PI 4.
I need to drop table when all the values in my status_s column are equal to "DONE". I cannot figure out how to drop table under a certain condition. MYSQL tables can be found here for testing:
https://www.db-fiddle.com/f/siZmmKWLjRDdpYX6deEPYF/1
Initially, the status_s values are not "DONE". As my program runs, the values update and eventually all of them will be "DONE", at that point, I do not want to have this table anymore as it is not important.
Thanks in advance
UPDATE Adding snippet of Python program
def update_data_when_complete(conn,table_name):
cur = conn.cursor()
sql = "SELECT COUNT(DISTINCT(ID)) = SUM(Status = 'DONE') FROM {table}"
cur.execute(sql.format(table=table_name))
complete_result = cur.fetchone()
conn.commit()
#print("COmplete result = ",complete_result[0])
# if complete_result[0] is 1 here, all rows are "DONE" and must delete table after few minutes
if(complete_result[0] == 1):
sql = "DROP TABLE {table}"
cur.execute(sql.format(table=table_name))
conn.commit()
else:
print("Table not fully complete yet")
Use Event Scheduler.
Create event procedure:
CREATE EVENT remove_temptable
ON SCHEDULE
EVERY 1 MINUTE
COMMENT 'Remove `temptable` when its `status_s` column is equal to "DONE" in all rows.'
DO
BEGIN
IF EXISTS ( SELECT NULL
FROM INFORMATOIN_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'my_database'
AND TABLE_NAME = 'temptable' ) THEN
IF !( SELECT SUM(status_s != 'DONE')
FROM my_database.temptable ) THEN
DROP TABLE my_database.temptable;
END iF;
END IF;
END;
This procedure will check the table temptable for its existence firstly. If it exists then the procedure checks does a row with non-NULL value in status_s column other than 'DONE' exists. If not then the procedure drops the table.
The procedure is executed each minute. You may adjust how often it is executed. Also, when it is created, you may enable or disable it using ALTER EVENT (for example you may enable it after temptable creation and disable after you ensure the table is dropped).
Do not forget to enable Event Scheduler.

SQL Event - DELETE AND UPDATE rows on tables after UPDATE other table

I'd like to have a tricky SQL statement as an Event that runs every couple of minutes.
Currently, I'm doing so with Java, using 3 separate statements that executing sequentiality in a transaction connection.
Q: I don't know how to construct such an SQL statement without Java. If impossible to have a single SQL statement, I'd like to use transaction (as I'm using in Java) and rollback in case of failure in any of those separate statements.
My Case:
I have 3 tables: "Factory", "Plan", "Machine".
I want to do something as below:
1.
WHERE Machines.annualCheck == "TRUE"
SET Machine.status = "IN_ANNUAL_CHECK"
For machines that got updated I need to do the following:
2.1 Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = --1
2.2 Delete the upcoming plans that planned to be handled by the related machine
DELETE rows WHERE Plan.willHandleByMachineID = Machine.ID
p.s. I'm using MySQL
Thank you!
Update:
In following to Simonare suggestion, I tired to do the following:
DELIMITER $
CREATE PROCEDURE annualCheck(IN Machine_ID int, IN Factory_ID int)
BEGIN
UPDATE machine_table
SET machine_table.annualCheck = 'IN_ANNUAL_CHECK'
WHERE machine_table.machine_id = Machine_ID;
UPDATE factory_table
SET factory_table.totalActiveMachines = factory_table.totalActiveMachines - 1
WHERE factory_table.factory_id = Factory_ID;
DELETE FROM plan_table WHERE plan_table.assign_to_machine = Machine_ID
END$
DELIMITER $$
BEGIN
SELECT #m_id = machine_id, #f_id = link_factory_id
FROM machine_table
WHERE machine_table.annualCheck = 'TRUE';
END$$
CALL annualCheck(#m_id,#f_id)
I don't know why, but I'm running into syntax errors - one after the other.
It's my first time to use PROCEDURE and DELIMITER. Am I doing it right?
you can use stored procedure
delimiter //
CREATE PROCEDURE myProc (IN Machine_ID int)
BEGIN
UPDATE myTable
SET Machine.status = "IN_ANNUAL_CHECK"
WHERE Machines.annualCheck == "TRUE";
Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = totalActiveMachines -1;
DELETE FROM Plan WHERE Plan.willHandleByMachineID = Machine_ID;
END//
then you can execute it either from mysql
CALL simpleproc(#a);
or from Java
It is also possible to create trigger on the Machine table, something like this:
CREATE TRIGGER `TRG_Machines_AfterUpdate` AFTER UPDATE ON `Machine` FOR EACH ROW BEGIN
IF OLD.annualCheck = 'TRUE' AND NEW.annualCheck = 'IN_ANNUAL_CHECK' THEN
UPDATE
Factory
SET
totalActiveMachines = totalActiveMachines - 1
WHERE
id = NEW.linkFactoryID
;
DELETE FROM
Plan
WHERE
willHandleByMachineID = NEW.ID
;
END;
END
So you can just issue normal update:
UPDATE Machine SET annualCheck = 'IN_ANNUAL_CHECK' WHERE annualCheck = 'TRUE'

MySQL Stored procedure SELECT var wrong value after INSERT

I have a problem on MySQL stored procedure, already did another logic with IF THEN ELSE, but I still have problems which I cant understand...
The procedure consists on two user inputs: user_id and meet_id. The procedure define a variable called 'ue' which stores result of a bunch of validation (if user exists, if event exists, if event date is still valid, etc.).
After that, it does INSERT and UPDATE some data on multiple tables in IF THEN ELSE selector, and SELECT 1 (or 0) AS result depending of validation.
But my problem is: it always return me 0 as 'result', as if my validation variable was 0 when I do INSERT... And there is where things get weird, if I remove the INSERT [...]; line of the code, it returns me the value of validation correctly (1 or 0).
The code of the procedure is this one:
CREATE DEFINER=`nwouserf`#`localhost`
PROCEDURE `join_event`(IN `user_id` BIGINT(64), IN `event_id` INT)
NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY DEFINER
begin
DECLARE ue INT;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE date > NOW() AND meet_id = event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND participants <= max_participants)
AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id);
IF ue = 1 THEN
INSERT INTO meetup_participation (fb_uid, meet_id) VALUES (user_id, event_id);
UPDATE users SET events_participated = events_participated + 1 WHERE fb_uid=user_id;
UPDATE meetup SET participants = participants + 1 WHERE meet_id=event_id;
SELECT 1 AS result;
ELSEIF ue = 0 THEN
SELECT 0 AS result;
ELSE
SELECT null AS result;
END IF;
end
Thanks in advance!
I have been stuck on this for a while now, and can not figure out why.
You should define OUT parameter. Add
", OUT result INT"
immediately after the last IN parameter.

Updating a column name of a same table which has a parent child relationship using mysql

I searched a lot of doing a task but found no appropriate solution.
Basically the scenario is. I have a user_comment table in which there are 5 column(id,parent_id,user_comments,is_deleted,modified_datetime). There is a parent child relationship like 1->2,1->3,2->4,2->5,5->7 etc. Now i am sending the id from the front end and i want to update the column is_deleted to 1 and modified_datetime on all the records on
this id as well as the all the children and children's of children.
I am trying to doing this by using a recursive procedure. Below is the code of my procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_id INT DEFAULT NULL ;
if(mode = 'delete')
then
update user_comment set is_deleted = 1, modified_datetime = now()
where id = comment_id ;
select id from user_comment where parent_id = comment_id into p_id ;
if p_id is not null
then
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_id);
end if;
end if;
END
By using this procedure it give me an error of more than one row.
If i return the select query without giving it to variable then shows me the the appropriate results on the select query but i have to call this procedure recursively based on getting the ids of the select query.
I need help i have already passed 2 days into this.
I used cursor also. Below is the code of cursor
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_emp int;
DECLARE noMoreRow INT;
DECLARE cur_emp CURSOR FOR select id from user_comment where parent_id = comment_id ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET noMoreRow = 0;
if(mode = 'delete')
then
OPEN cur_emp;
LOOPROWS: LOOP
IF noMoreRow = 0 THEN
update user_comment set is_deleted = 1, modified_datetime = now() where id = comment_id
CLOSE cur_emp;
LEAVE LOOPROWS;
END IF;
FETCH cur_emp INTO p_emp;
update user_comment set is_deleted = 1, modified_datetime = now() where id = p_emp ;
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_emp);
END LOOP;
end if;
END
After using cursor i am getting a thread error.i don't know how can overcome this problem!!!
Mysql's documentation on select ... into varlist clearly says:
The selected values are assigned to the variables. The number of
variables must match the number of columns. The query should return a
single row. If the query returns no rows, a warning with error code
1329 occurs (No data), and the variable values remain unchanged. If
the query returns multiple rows, error 1172 occurs (Result consisted
of more than one row). If it is possible that the statement may
retrieve multiple rows, you can use LIMIT 1 to limit the result set to
a single row.
Since you wrote in the OP that a comment can be parent of many comments, using simple variables cannot be a solution. You should use a CURSOR instead, that can store an entire resultset.
You loop through the records within the cursos as shown in the sample code in the above link and call user_comments() in a recursive way.
UPDATE
If your receive
Error Code: 1436. Thread stack overrun
error, then you can do 2 things:
Increase the thread_stack setting in the config file and restart mysql server.
You can try to simplify your code to use less recursions and therefore less stack space. For example, when you fetch all children into the cursor, then rather calling the user_comments() recursively for each, you can set all direct children's status within the code and call the function recirsively on grand-childrens only (if any). You can also change your data structure and use nested set model to approach hierarchical structures.
Nested set model is more complex to understand, it is less resource intensive to traverse, but more resource intensive to maintain.

Trigger: Referencing updated attribute

I have a Trigger on UPDATE.
What is the correct procedure for referencing attribute from the table that is not updated by the UPDATE SQL command? Is the attribute still in the UPDATE variable? I would like to get the value of that attribute for the updated row.
You can access a values of a column before update and after update in MySQL by using keywords OLD and NEW.
For example if you want to determine whether a value of a column actually has been changed during updated you can do
IF NOT OLD.column_name <=> NEW.column_name THEN
-- do something here
END IF;
Note: <=> is NULL-safe comparison operator in MySQL
BTW: There is no UPDATED virtual table in MySQL. It's from SQL Server.
Here is a SQLFiddle demo. Note that even though update affected all records in the table, only one message has been logged in log table. It's because value for a row with id 2 in the end stayed the same.
UPDATE: to keep your finished flag in sync you need triggers for all events (insert, update, delete).
DELIMITER //
CREATE TRIGGER tg_ai_event
AFTER INSERT ON event
FOR EACH ROW
BEGIN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = NEW.activity;
END//
CREATE TRIGGER tg_ad_event
AFTER DELETE ON event
FOR EACH ROW
BEGIN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = OLD.activity;
END//
CREATE TRIGGER tg_au_event
AFTER UPDATE ON event
FOR EACH ROW
BEGIN
IF NOT OLD.activity <=> NEW.activity THEN
-- if activity id was changed for an event then clculate finished flag
-- for both old and new activity id
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id IN(OLD.activity, NEW.activity);
ELSE
-- otherwise calculate finished flag only if done flag is changed
IF NOT OLD.done <=> NEW.done THEN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = NEW.activity;
END IF;
END IF;
END//
DELIMITER ;
Here is SQLFiddle demo