I have a table autos that has a column name, I want to check first 5 rows in the table and if name value is "toyota", in table mytable write "yes", else write "no".
I write stored procedure, but mysqli_error() returns error in line, where I have EXECUTE ....
If in WHEN I write not PREPARED STATEMENT, but directly the query, the procedure works.
Please see my code and tell me, where is it wrong?
CREATE PROCEDURE proc_auto()
BEGIN
DECLARE start INT;
SET start = 0;
PREPARE stmt FROM ' SELECT name FROM autos ORDER BY id LIMIT ?,1 ';
WHILE start < 5 DO
CASE
WHEN (EXECUTE stmt USING #start ) = 'toyota'
THEN INSERT INTO mytable (log) VALUES('yes');
ELSE
INSERT INTO mytable (log) VALUES('no');
END CASE;
SET start = start + 1;
END WHILE;
END;
(The suggestion about EXECUTE is removed as incorrect and potentially confusing.)
The problem you are trying to solve with a stored procedure could in fact be solved without it, using an entirely different approach: just use a single INSERT ... SELECT statement instead:
INSERT INTO mytable (log)
SELECT
CASE name
WHEN 'toyota' THEN 'yes'
ELSE 'no'
END
FROM autos
ORDER BY id
LIMIT 5
That is, the above statement does the same as your stored procedure: it retrieves first 5 rows from autos and inserts 5 rows into mytable. Depending on the value of name it generates either yeses or nos.
Andriy M's INSERT statement is the most elegant solution, but if you still want to use a procedure, this will work:
CREATE PROCEDURE proc_auto()
BEGIN
DECLARE start INT DEFAULT 0;
PREPARE stmt FROM
'SELECT name INTO #name FROM autos ORDER BY id LIMIT ?,1';
WHILE start < 5 DO
SET #start = start;
EXECUTE stmt USING #start;
IF #name = 'toyota' THEN
INSERT INTO mytable (log) VALUES('yes');
ELSE
INSERT INTO mytable (log) VALUES('no');
END IF;
SET start = start + 1;
END WHILE;
END;
but, in this case, using a CURSOR would yield better performance:
CREATE PROCEDURE proc_auto()
BEGIN
DECLARE start INT DEFAULT 0;
DECLARE b_not_found BOOL DEFAULT FALSE;
DECLARE cur CURSOR FOR
'SELECT name FROM autos ORDER BY id LIMIT 5';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b_not_found = TRUE;
OPEN cur;
loop1: WHILE start < 5 DO
FETCH cur INTO #name;
IF b_not_found THEN
LEAVE loop1;
END IF;
IF #name = 'toyota' THEN
INSERT INTO mytable (log) VALUES('yes');
ELSE
INSERT INTO mytable (log) VALUES('no');
END IF;
SET start = start + 1;
END WHILE;
CLOSE cur;
END;
Related
When updating data in database tables, I need to write to a separate table: table name, change date, old column value, new column value.
I wrote a trigger:
CREATE TRIGGER `user_update_trigger`
AFTER UPDATE ON `users`
FOR EACH ROW
BEGIN
DECLARE done int default false;
DECLARE col_name CHAR(255);
DECLARE counter INTEGER(11);
DECLARE column_cursor cursor for SELECT `column_name`
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='test'
AND `TABLE_NAME`='users';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open column_cursor;
myloop: loop
fetch column_cursor into col_name;
if done then
leave myloop;
end if;
/*SET #old_val = OLD.{{col_name}}; <------ HERE */
/*SET #new_val = NEW.{{col_name}};<------ HERE */
if #old_val <> #new_val then
/*INSERT INTO `logs` ....*/
end if;
end loop;
close column_cursor;
END;
I need to get the value from OLD and NEW.
But column name is in the variable col_name.
I tried:
SET #old_q = CONCAT('OLD.', col_name);
SET #new_q = CONCAT('NEW.', col_name);
PREPARE old_prepare_query FROM #old_q;
PREPARE new_prepare_query FROM #new_q;
EXECUTE old_prepare_query USING #old_val;
EXECUTE new_prepare_query USING #new_val;
But I got an error:
Error Code: 1336. Dynamic SQL is not allowed in stored function or trigger
Here is my simplified, the value of rent_ids string returns to null after the loop. I already know that the loop is working properly and that the value of rent_ids changes with every itiration.
BEGIN
DECLARE rent_ids VARCHAR(265);
DECLARE tmp_rent_id int;
create temporary table due_rent_ids (rent_id int);
SET rent_ids = "";
set #test = "Insert into due_rent_ids (rent_id) select unit_id from tbl_rent";
PREPARE stmt1 FROM #test;
EXECUTE stmt1;
BEGIN
DECLARE cur1 CURSOR for select rent_id from due_rent_ids;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tmp_rent_id;
IF rent_ids = "" THEN
SET rent_ids = tmp_rent_id;
ELSE
SET rent_ids = concat(rent_ids, ", ", tmp_rent_id);
END IF;
END LOOP;
CLOSE cur1;
END;
select * from tbl_unit where unit_id in (rent_ids);
DEALLOCATE PREPARE stmt1;
END
You're doing this all wrong. You can't put a comma-separated string in IN (...), the commas have to be in the actual SQL code.
The right way to do this is:
SELECT *
FROM tbl_unit
WHERE unit_id IN (SELECT rent_id FROM due_rent_ids)
Or:
SELECT t1.*
FROM tbl_unit AS t1
JOIN due_rent_ids AS t2 ON t1.unit_id = t2.rent_id
The second form tends to perform better in MySQL.
Data fetched from cursor is NULL. But number of row my query select id from Projects returning is correct. I have put a select statement in my procedure loop to debug. It's returning null for id.
I have followed this [https://dev.mysql.com/doc/refman/5.5/en/cursors.html]
What's wrong with this code?
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ReportDiffProDiffFinYear`()
BEGIN
DECLARE id INT;
DECLARE sqlstr varchar(10000) default "select financialYear as 'name' ";
DECLARE done INT DEFAULT FALSE;
DECLARE cur1 CURSOR FOR select id from Projects; /* My CURSOR QUERY*/
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id;
select id;
IF done THEN
LEAVE read_loop;
END IF;
set sqlstr = concat(sqlstr, ", sum(if(projects_id=",id,",0,1)) ");
END LOOP;
CLOSE cur1;
set #sqlstr = concat(sqlstr," from (select * from WaterPoints where status='Waterpoint Complete') as wp, (select id,financialYear from ProjectDetails) as pd where pd.id=wp.projectDetails_id group by pd.financialYear");
prepare stmt from #sqlstr;
execute stmt;
deallocate prepare stmt;
END
I believe that your variable name id clashes with the query for the cursor, therefore the cursor query fetches the value of the id variable (null), not the value of the id from the projects table. Either change the name of the variable or use projects.id to reference the column in the query.
I'm working on a fairly simple ticket managment system. I want to keep a log for stuff that gets added, deleted, and changed.
I created three triggers, AFTER INSERT, AFTER DELETE, and AFTER UPDATE. The INSERT/DELETE triggers are straightforward, it's theUPDATE trigger I'm having problems with.
I would like to add which columns has changed in the table with their old & new values, i.e. colname changed from X to Y
The trigger I have now "works", except of course that it doesn't insert the actual values I'd like.
How do I get the value from OLD and NEW using the col_name variable?
I'm also not sure if this is the best possible way of doing this ... So if anyone has ideas on that, they're welcome too ... This trigger started out a lot simpler ...
BEGIN
DECLARE num_rows, i int default 1;
DECLARE col_name CHAR(255);
DECLARE updated TEXT;
DECLARE col_names CURSOR FOR
SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'storing'
ORDER BY ordinal_position;
OPEN col_names;
SELECT FOUND_ROWS() INTO num_rows;
SET i = 1;
SET #updated = 'Updated columns: ';
the_loop: LOOP
IF i > num_rows THEN
LEAVE the_loop;
END IF;
FETCH col_names INTO col_name;
/* So, how do I get the proper values? */
/* IF NEW.#col_name != OLD.#col_name THEN */
/*SET #updated = CONCAT(#updated, OLD.#col_name, ' changed into ', NEW.#col_name, ' ');*/
SET #updated = CONCAT(#updated, 'OLD', ' changed into ', 'NEW', ' ');
/* END IF;*/
SET i = i + 1;
END LOOP the_loop;
CLOSE col_names;
INSERT INTO `log` (`storing`, `medewerker`, `actie`, `data`)
VALUES (NEW.`id`, NEW.`medewerker`, "Storing aangepast", #updated);
END
Since usage of prepared statements here is impossible, I would suggest you to call some INSERT statements, e.g. -
IF NEW.column1 <> OLD.column1 THEN
INSERT INTO...
END IF;
IF NEW.column2 <> OLD.column2 THEN
INSERT INTO...
END IF;
...
Or try to copy all fields you need into another table.
In these cases you will avoid using cursor.
Try to use prepared statements
Something like this:
SET #s = CONCAT('SELECT new.', #col_name, ', old.', #col_name, ' FROM ', /*here is the query details like inner joins etc.*/, ' where ', 'NEW.', #col_name, '!= OLD.', #col_name )
PREPARE stmt FROM #s;
EXECUTE stmt;
I have a simple insert select which insert _TABLE_B_ data in _TABLE_A_ new row
INSERT INTO _TABLE_A_(_USERNAME_,_ID_)
SELECT _USERNAME_,_ID_
FROM _TABLE_B_
I want to insert a row in a table named _TABLE_C_ each time i insert a row in _TABLE_A_ and add the current inserted _TABLE_C_ id in _TABLE_A_.
i'll try to explain it in an other way :
INSERT INTO _TABLE_A_(_USERNAME_,_ID_,_FOREIGN_ID_)
SELECT B._USERNAME_,B._ID_,C._FOREIGN_ID_
FROM _TABLE_B_ AS B
LEFT JOIN _TABLE_C_ AS C
#Insert a row in _TABLE_C_ to retrieve _FOREIGN_ID_...
I'm searching for a single minimal query which have the INSERT SELECT statement like mine because insert select can loop and i have to loop.
FYI :
I'm in a stored procedure.
I also use prepared statements with dynamic data, and cursors is not suitable for dynamic data select...
I would do all the INSERTs in _TABLE_C_ first and then join it in the INSERT _TABLE_A_ to get the appropriate foreign keys.
If that is not possible, I would use a cursor.
Cursor on _TABLE_B_ & Fetch
INSERT _TABLE_C_
INSERT _TABLE_A_ with Foreign_Id = SCOPE_IDENTITY()
Fetch next
I found a solution.
create a temporary table and add dynamic select statement which retrieve the primary keys (id)
declare a cursor and select this temporary table id ( variables doesn't work but temporary tables do )
execute statement to create temporary table
open the cursor and iterate the inserts
EXAMPLE
BEGIN
DECLARE isDone INT DEFAULT 0;
DECLARE fetchedmemberWhoWillReceiveMailId int;
DECLARE cur1 CURSOR FOR SELECT id FROM memberWhoWillReceiveMail;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET isDone = 1;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET #sexe = VAR_sexe;
SET #event = VAR_eventId;
SET #subject = VAR_subject;
SET #body = VAR_body;
SET #to = VAR_to;
SET #from = VAR_from;
SET #region = VAR_region;
SET #departement = VAR_departement;
SET #age = VAR_age;
SET #baseSqlStatement =' CREATE TEMPORARY TABLE memberWhoWillReceiveMail SELECT e.id FROM TABLE_A as e LEFT JOIN TABLE_B AS a on a.member_id = e.id';
SET #whereSqlStatement= 'WHERE e.is_visible = 1 AND e.member_group_id IN (10,11) ';
IF (#region!='') THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND region=',#region);
END IF;
IF (#event !=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND m.event_id !=' ,#eventId);
END IF;
IF (#sexe!=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND e.sexe=',#sexe);
END IF;
SET #baseSqlStatement = CONCAT(#baseSqlStatement,#whereSqlStatement);
START TRANSACTION;
PREPARE stmt1 FROM #baseSqlStatement;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
OPEN cur1;
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId;
WHILE NOT isDone DO
INSERT INTO conversation(created_at,updated_at)VALUES(now(),now());
INSERT INTO message(created_at,updated_at,from, to, uniqueID) VALUES(now(),now(),#from,fetchedmemberWhoWillReceiveMailId,LAST_INSERT_ID() );
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId; END WHILE; CLOSE cur1;
COMMIT;
DROP TEMPORARY TABLE IF EXISTS memberWhoWillReceiveMail;
END