MySQL trigger end if syntax error - mysql

Unable to create trigger on update of specific field
delimiter //
CREATE TRIGGER `add_event` AFTER UPDATE ON `order_table`
FOR EACH ROW
IF NEW.status <=> OLD.status THEN
INSERT INTO `events` SET
events.status = NEW.status,
events.order_id = NEW.order_id,
events.time_stamp = CURRENT_TIMESTAMP
END IF;
delimiter ;
I am getting this error
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
near 'END IF;

There is no syntax error in below mysql trigger.
delimiter //
CREATE TRIGGER add_event AFTER UPDATE ON order_table
FOR EACH ROW
BEGIN
IF NEW.status <=> OLD.status THEN
INSERT INTO `events`
SET events.status = NEW.status,
events.order_id = NEW.order_id,
events.time_stamp = CURRENT_TIMESTAMP;
END IF;
END;//
delimiter ;

drop table if exists t;
drop table if exists events;
create table t(order_id int, status varchar(1));
create table events(order_id int, time_stamp timestamp,status varchar(1));
drop trigger if exists t;
delimiter //
CREATE TRIGGER t after update ON `t`
FOR EACH ROW
begin
IF NEW.status <> OLD.status THEN
INSERT INTO `events`
SET events.status = NEW.status,
events.order_id = NEW.order_id,
events.time_stamp = CURRENT_TIMESTAMP;
END IF ;
end //
delimiter ;
insert into t values(1,1);
insert into events values(1,now(),1);
update t set status = 2 where order_id = 1;
select * from t;
select * from events;
MariaDB [sandbox]> select * from t;
+----------+--------+
| order_id | status |
+----------+--------+
| 1 | 2 |
+----------+--------+
1 row in set (0.00 sec)
MariaDB [sandbox]> select * from events;
+----------+---------------------+--------+
| order_id | time_stamp | status |
+----------+---------------------+--------+
| 1 | 2018-08-06 10:16:35 | 1 |
| 1 | 2018-08-06 10:16:35 | 2 |
+----------+---------------------+--------+
2 rows in set (0.00 sec)

Why are you using if statement. You are checking everything <=>. Here is the solution :
CREATE TRIGGER `add_event` AFTER UPDATE ON `order_table`
FOR EACH ROW
BEGIN
INSERT INTO `events` SET events.status = NEW.status, events.order_id = NEW.order_id, events.time_stamp = CURRENT_TIMESTAMP;
END;
delimiter ;

Related

value of variable returned from a query (with empty result set)?

I have a mysql query written as part of a Stored Procedure
-- {parameter}
SELECT id INTO emailId FROM email_list WHERE email = {email};
If this query returns an empty result set, what is the value of emailId? undefined, null, 0
I want to check if emailId contains a value and run a series of inserts.
I could use COUNT or IFNULL but that would mean declaring another variable to check against. I want to avoid it if possible.
***************************** EDIT ****************************
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCustomer`(
IN email1 VARCHAR(60),
IN status1 VARCHAR(15),
IN bill_pin_id SMALLINT UNSIGNED,
IN bill_addr VARCHAR(175),
IN bill_name VARCHAR(70),
IN tel VARCHAR(15)
)
BEGIN
DECLARE emailId INT UNSIGNED DEFAULT 0;
DECLARE billAddrId INT UNSIGNED DEFAULT 0;
DECLARE custId INT UNSIGNED DEFAULT 0;
DECLARE orderId INT UNSIGNED DEFAULT 0;
DECLARE sqlError TINYINT DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET sqlError = true;
-- get id of email if it exists
SELECT id INTO emailId FROM email_list WHERE email = email1;
-- if email exists, check if a customer is listed
IF (emailId > 0) THEN
SELECT id INTO custId FROM customer WHERE email_list_id = emailId;
IF (custId > 0) THEN
-- check for duplicate address in the address table
SELECT address_id INTO billAddrId FROM customer_addr
INNER JOIN address ON address_id = address.id
WHERE customer_id = custId AND address = bill_addr;
END IF;
END IF;
START TRANSACTION;
-- if emails isnt listed - no customer or address should exist
IF (emailId = 0) THEN
INSERT INTO email_list (email, status) VALUES (email1, status1);
SELECT LAST_INSERT_ID() INTO emailId;
SELECT emailId;
INSERT INTO customer (email_list_id, full_name, phone) VALUE (emailId, bill_name, tel);
SELECT LAST_INSERT_ID() INTO custId;
SELECT custId;
INSERT INTO address (pincode_id, address) VALUES (bill_pin_id, bill_addr);
SELECT LAST_INSERT_ID() INTO billAddrId;
SELECT billAddrId;
INSERT INTO customer_addr (address_id, customer_id) VALUES (billAddrId, custId);
SELECT LAST_INSERT_ID();
INSERT INTO orders (customer_id, order_status) VALUES (custId, 'pending');
SELECT LAST_INSERT_ID() INTO orderId;
END IF;
IF sql_error = FALSE THEN
COMMIT;
SELECT 'SUCCESS';
ELSE
ROLLBACK;
SELECT 'FAILED';
END IF;
END$$
DELIMITER ;
I removed the unnecessary lines in the code to keep it short.
Here is the values returned from the select statements. All tables are empty. But the below values indicate rows have been inserted. But it is still empty.
emailId
43
custId
12
billAddrId
13
CustomerAddr
13
orderId
8
You can test it by selecting the value:
drop procedure if exists test_proc;
create procedure test_proc()
begin
DECLARE emailId INT UNSIGNED;
SET emailId = 0;
SELECT 123 INTO emailId FROM dual WHERE 1 = 2;
SELECT emailId;
end;
call test_proc();
This will return 0, which means - the value is not changed if no row has been found.
Demo: http://rextester.com/MHRTL99697
Works for me
MariaDB [sandbox]> drop procedure if exists p;
Query OK, 0 rows affected (0.06 sec)
MariaDB [sandbox]> delimiter $$
MariaDB [sandbox]> create procedure p(inid int)
-> begin
-> declare vid int;
-> set vid = 0;
-> select id into vid from users where id = inid;
-> if vid = 1 then
-> select 'found';
-> else
-> select 'notfound';
-> end if;
-> end $$
Query OK, 0 rows affected (0.05 sec)
MariaDB [sandbox]> delimiter ;
MariaDB [sandbox]>
MariaDB [sandbox]> call p(1);
+-------+
| found |
+-------+
| found |
+-------+
1 row in set (0.02 sec)
Query OK, 0 rows affected (0.02 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> call p(1000);
+----------+
| notfound |
+----------+
| notfound |
+----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected, 1 warning (0.01 sec)
MariaDB [sandbox]> show warnings;
+---------+------+-----------------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------------+
| Warning | 1329 | No data - zero rows fetched, selected, or processed |
+---------+------+-----------------------------------------------------+
1 row in set (0.00 sec)

How to update table using execute statement?

I have two tables (much simplified here):
QUADRI
ID SBR_750 b10C TGI
---------------------
Q1 0 1 0
Q2 2 1 0
Q3 1 0 1
CELLE
CELLANAME NEEDED READY
----------------------
SBR_750 NULL 12
b10C NULL 10
TGI NULL 5
I want this result in CELLE:
CELLANAME NEEDED READY
------------------------
SBR_750 3 12
b10C 2 10
TGI 1 5
I tried to write a stored procedure but it doesn't works: ERROR 1210. Incorrect arguments to EXECUTE.
Here is the code:
CREATE DEFINER=`root`#`%.zamberlan.local` PROCEDURE `AggiornaCelle`(IN nomecella varchar(15))
BEGIN
set #s='update celle set needed=(select sum(?) from quadri) where cellaname=?';
set #NC=nomecella;
prepare stmt from #s;
execute stmt using #NC;
deallocate prepare stmt;
END
UPDATE:
It doesn't work so I change strategy:
CREATE DEFINER=`root`#`%.zamberlan.local` PROCEDURE `AggiornaCelle`()
BEGIN
declare i int;
declare num_rows int;
declare col_name varchar(20);
DECLARE col_names CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = quadri
ORDER BY ordinal_position;
select FOUND_ROWS() into num_rows;
SET i = 1;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
update celle set needed=sum(col_name) where cellaname=col_name;
SET i = i + 1;
END LOOP the_loop;
END
inspired by mysql, iterate through column names.
However I receive the error "Cursor is not open..."
You do need to use dynamic sql to do this. Celle knows about all the columns it needs from quadri so you could drive the creation of the dynamic statements from this fact. Using a cursor is as good a way to do it as any.
drop table if exists quadri;
create table quadri(ID varchar(2),SBR_750 int, b10C int, TGI int);
insert into quadri values
('Q1' , 0 , 1 , 0),
('Q2' , 2 , 1 , 0),
('Q3' , 1 , 0 , 1);
drop table if exists celle;
create table CELLE (CELLANAME varchar(20) ,NEEDED int,READY int);
insert into celle values
('SBR_750' , NULL , 12),
('b10C' , NULL , 10),
('TGI' , NULL , 5);
drop procedure if exists `AggiornaCelle`;
delimiter $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `AggiornaCelle`()
begin
DECLARE done INT DEFAULT FALSE;
declare col_name varchar(20);
declare cur1 CURSOR FOR
SELECT cellaname FROM celle ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open cur1;
read_loop: loop
fetch cur1 into col_name;
if done then leave read_loop; end if;
set #sqlstr = concat('update celle set needed = (select sum(',col_name,') from quadri) where cellaname = ', char(39),col_name,char(39),';');
insert into debug_table (msg) values(#sqlstr);
prepare stmt from #sqlstr;
execute stmt ;
deallocate prepare stmt;
end loop;
close cur1;
end $$
delimiter ;
truncate table debug_table;
call `AggiornaCelle`();
select * from debug_table;
select * from celle;
MariaDB [sandbox]> select * from debug_table;
+----+------------------------------------------------------------------------------------------+------+
| id | msg | MSG2 |
+----+------------------------------------------------------------------------------------------+------+
| 1 | update celle set needed = (select sum(SBR_750) from quadri) where cellaname = 'SBR_750'; | NULL |
| 2 | update celle set needed = (select sum(b10C) from quadri) where cellaname = 'b10C'; | NULL |
| 3 | update celle set needed = (select sum(TGI) from quadri) where cellaname = 'TGI'; | NULL |
+----+------------------------------------------------------------------------------------------+------+
3 rows in set (0.00 sec)
MariaDB [sandbox]> select * from celle;
+-----------+--------+-------+
| CELLANAME | NEEDED | READY |
+-----------+--------+-------+
| SBR_750 | 3 | 12 |
| b10C | 2 | 10 |
| TGI | 1 | 5 |
+-----------+--------+-------+
3 rows in set (0.00 sec)
The debug_table only exists so that I can check the update statements.
As per documentation
set #s='update celle set needed=(select sum(?) from quadri) where cellaname=?';
You are supposed to pass two argument.
execute stmt using #NC;
should be or something similar
execute stmt using #NC, #NC;
#NC would be same as you are trying to update a row in celle, which is same as column name in quadri table.

Mirror tables: triggers, deadlock and implicit commits

I'm have 2 similar tables, for example A and B. I want to replicate insertions in A to B, and insertions in B to A to integrate two users systems . I configured "after insert triggers" on each one. Example:
DELIMITER $$
CREATE DEFINER = `root`#`localhost` TRIGGER
`after_A_INSERT`
AFTER INSERT ON `A`
FOR EACH ROW BEGIN
INSERT INTO `B`
SET `id` = NEW.`id`,`name` = NEW.`name`;
END$$
DELIMITER ;
DELIMITER $$
CREATE DEFINER = `root`#`localhost` TRIGGER
`after_B_INSERT`
AFTER INSERT ON `B`
FOR EACH ROW BEGIN
INSERT INTO `A`
SET `id` = NEW.`id`,`name` = NEW.`name`;
END$$
DELIMITER ;
If I insert in A, the triggers calls an insert in B, but this insert executes the trigger in B and a deadlock occurs avoiding a infinite loop.
I've tried to edit triggers to DROP the another table trigger before do the INSERT and then CREATE it again after it. Example:
DELIMITER $$
CREATE DEFINER = `root`#`localhost` TRIGGER
`after_B_INSERT`
AFTER INSERT ON `B`
FOR EACH ROW BEGIN
DROP TRIGGER IF EXISTS `after_A_INSERT`;
INSERT INTO `A`
SET `id` = NEW.`id`, `name` = NEW.`name`;
/* And CREATE again here */
END$$
DELIMITER ;
However CREATE is a Data Definition Language (DDL) statement that makes an implicit commit. Thus, this can't be done.
I've tried to call a PROCEDURE with the DROP inside to handle explicitly the commit, but isn't possible too.
Any suggestion to mirror this 2 tables?
UPDATE: Using Bill Karwin suggestion, I added a origin field to each table with a respective default vale A or B. Then, I alter (DROP and reCREATE) the triggers as follows:
Trigger in A:
...
BEGIN
IF NEW.`origin`='A' THEN
INSERT INTO `B`
SET `id` = NEW.`id`, `name` = NEW.`name`, `origin` = NEW.`origin`;
END IF;
END
Trigger in B:
...
BEGIN
IF NEW.`origin`='B' THEN
INSERT INTO `A`
SET `id` = NEW.`id`, `name` = NEW.`name`, `origin` = NEW.`origin`;
END IF;
END
You need some way to avoid creating a cycle.
I'd suggest adding a column origin in both tables. In table A, make the DEFAULT 'A'. In table B, make the DEFAULT 'B'.
When inserting to either table in your application, always omit the origin column, allowing it to take its default value.
In both triggers, replicate to the other table only if the NEW.origin is equal to the respective table's default.
Re your comment and new error:
Sorry, I forgot to mention that in the trigger when inserting to the other table, you must also copy the value of NEW.origin. Just in your application when you do the original insert do you omit origin.
Trigger in A:
...
BEGIN
IF NEW.`origin`='A' THEN
INSERT INTO `B`
SET `id` = NEW.`id`, `name` = NEW.`name`, `origin` = NEW.`origin`;
END IF;
END
Trigger in B:
...
BEGIN
IF NEW.`origin`='B' THEN
INSERT INTO `A`
SET `id` = NEW.`id`, `name` = NEW.`name`, `origin` = NEW.`origin`;
END IF;
END
I created these triggers and then tested:
mysql> insert into A set name = 'bill';
Query OK, 1 row affected (0.00 sec)
mysql> insert into B set name = 'john';
Query OK, 1 row affected (0.01 sec)
mysql> select * from A;
+----+------+--------+
| id | name | origin |
+----+------+--------+
| 1 | bill | A |
| 2 | john | B |
+----+------+--------+
2 rows in set (0.00 sec)
mysql> select * from B;
+----+------+--------+
| id | name | origin |
+----+------+--------+
| 1 | bill | A |
| 2 | john | B |
+----+------+--------+

Mysql Trigger with IF THEN

My storage is INNODB,
I'm trying to create an trigger with 2 queries in IF statement.
Down you can see the trigger that gives me the error
delimiter |
CREATE TRIGGER count_delete_videos BEFORE DELETE ON videos
FOR EACH ROW BEGIN
UPDATE counts SET count = count - 1 WHERE name = 'all';
IF OLD.published = 1 THEN
DELETE FROM videos_categories WHERE id_video = OLD.id;
DELETE FROM videos_tags WHERE id_video = OLD.id;
END IF;
END;
|
delimiter ;
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '= OLD.id;
END IF;
END' at line 6
This are the 2 triggers that i activate with the first one.
delimiter |
CREATE TRIGGER count_delete_videos_tags AFTER DELETE ON videos_tags
FOR EACH ROW BEGIN
UPDATE tags SET count = count - 1 WHERE id = OLD.id_tag;
END;
|
delimiter ;
delimiter |
CREATE TRIGGER count_delete_videos_categories AFTER DELETE ON videos_categories
FOR EACH ROW BEGIN
UPDATE categories SET count = count - 1 WHERE id = OLD.id_category;
IF OLD.id_category <> 20 AND OLD.id_category <> 34 THEN
UPDATE counts SET count=count-1 WHERE name='english';
ELSEIF OLD.id_category = 34 THEN
UPDATE counts SET count=count-1 WHERE name='german';
ELSEIF OLD.id_category = 20 THEN
UPDATE counts SET count=count-1 WHERE name='italian';
END IF;
END;
|
delimiter ;
But this one works perfectly
delimiter |
CREATE TRIGGER count_delete_videos BEFORE DELETE ON videos
FOR EACH ROW BEGIN
UPDATE counts SET count = count - 1 WHERE name = 'all';
IF OLD.published = 1 THEN
DELETE FROM videos_categories WHERE id_video = OLD.id;
END IF;
END;
|
delimiter ;
Query OK, 0 rows affected (0.16 sec)
How can i make first trigger work? what i'm doing wrong?
Thx for helping me.
As far as I can tell both triggers are OK, but you might try the following:
DELIMITER $$
CREATE TRIGGER count_delete_videos BEFORE DELETE ON videos
FOR EACH ROW
BEGIN
UPDATE counts SET count = count - 1 WHERE name = 'all';
IF OLD.published = 1 THEN BEGIN
DELETE FROM videos_categories WHERE id_video = OLD.id;
DELETE FROM videos_tags WHERE id_video = OLD.id;
END; END IF;
END$$
DELIMITER ;

MySQL stored procedure giving different results to command line

I have a simple MySQL stored procedure designed to return all child records for a given node.
My problem is when I type this manually it returns the proper results - but when I put the same code into a Stored Procedure it returns only the parent id.
I'd really appreciate some guidance!
For example - when I call my procedure (code is below) I get:
call find_child(1006);
+--------+
| nodeid |
+--------+
| 1006 |
| 1006 |
| 1006 |
| 1006 |
+--------+
4 rows in set (0.01 sec)
BUT - When I cut and paste the command I get the proper resultset:
mysql> create temporary table KID_TABLE (nodeid INT);
Query OK, 0 rows affected (0.00 sec)
mysql> insert ignore into KID_TABLE (nodeid) select nodeid from CORPORATENODE
where parentid in (1006);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from KID_TABLE;
+--------+
| nodeid |
+--------+
| 1007 |
| 1008 |
| 1031 |
| 1038 |
+--------+
4 rows in set (0.00 sec)
Here's the code:
DELIMITER $$
DROP PROCEDURE IF EXISTS `find_child`$$
CREATE PROCEDURE `find_child`( IN NodeID INT)
DETERMINISTIC
BEGIN
declare nid INT;
set nid= NodeID;
create temporary table KID_TABLE (nodeid INT);
insert ignore into KID_TABLE (nodeid) select nodeid
from CORPORATENODE where parentid in (1006);
select * from KID_TABLE;
drop table KID_TABLE;
END $$
DELIMITER ;
Here's the DDL for PARENT table
CREATE TABLE `PARENT` (
`NODEID` int(11) NOT NULL AUTO_INCREMENT,
`PARENTID` int(11) NOT NULL DEFAULT '0' COMMENT '0 value means top node',
`NAME` varchar(50) NOT NULL,
PRIMARY KEY (`NODEID`) USING BTREE
) ENGINE=InnoDB;
Mysql will return your in variable NodeId in your select nodeid
from CORPORATENODE where parentid in (1006);
Change the variable to in_NodeID instead.
DELIMITER $$
DROP PROCEDURE IF EXISTS `find_child`$$
CREATE PROCEDURE `find_child`( IN in_NodeID INT)
BEGIN
create temporary table KID_TABLE (nodeid INT);
insert ignore into KID_TABLE (nodeid) select nodeid
from CORPORATENODE where parentid in (in_NodeID);
select * from KID_TABLE;
drop table KID_TABLE;
END $$
DELIMITER ;
But then of course, why use a temporary table?
DELIMITER $$
DROP PROCEDURE IF EXISTS `find_child`$$
CREATE PROCEDURE `find_child`( IN in_NodeID INT)
BEGIN
select nodeid from CORPORATENODE where parentid in (in_NodeID);
END $$
DELIMITER ;
Thanks Andreas et al,
I found a workaround to this just after posting - typical.
But couldn't post my solution till now.
I use Linux but have lower_case_table_names=1 set so the case is irrelevant. (I think?)
Andreas, I needed the temporary table as once I had selected back all the child records for a Node I then needed to update all the child records in the table NODEVERSION. I was running this via a trigger on the NODEVERSION table and was running into problems as I was updating the calling table etc. That's why I created a new CHILD_TABLE.
So I found that (even leaving DETERMINISTIC in... sorry pilcrow!)
The following worked as expected - all I did was added aliases ...
(The code below is a little different from above as I simplified the above code for my question)
Don't fully understand why the aliases fixed the initial problem - but it worked. Perhaps you experts know why?
CREATE PROCEDURE `find_child`( IN NodeID int)
DETERMINISTIC
BEGIN
declare nid INT;
declare rows_before INT DEFAULT 0;
declare rows_after INT DEFAULT 0;
set nid= NodeID;
delete from CHILD_TABLE;
insert into CHILD_TABLE values (nid);
set rows_before = (select count(*) from CHILD_TABLE);
if rows_before !=rows_after then
increment: repeat
set rows_before = (select count(*) from CHILD_TABLE);
insert ignore into CHILD_TABLE (nodeid) select b.nodeid from CORPORATENODE b
where b.parentid in (select c.nodeid from CHILD_TABLE c);
set rows_after= (select count(*) from CHILD_TABLE);
until rows_before =rows_after
end repeat increment;
end if;
update NODEVERSION n set STATUS=1 where n.nodeid in
(select c.nodeid from CHILD_TABLE c);
END