I have the following tables
CREATE TABLE `trigger_root` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`p` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `trigger_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`p` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
and the following triggers
DELIMITER ||
CREATE TRIGGER tit
BEFORE INSERT ON trigger_root
FOR EACH ROW
BEGIN
INSERT INTO trigger_test (p) values (NEW.p);
END ||
CREATE TRIGGER tdt
BEFORE delete ON trigger_root
FOR EACH ROW
BEGIN
delete from trigger_test where p=OLD.p;
END ||
DELIMITER ;
However if I use the following statement
replace into trigger_root(id,p) select id,p from trigger_root;
only the delete trigger is called. if i remove the delete trigger the insert trigger is called.
so it seems replace only triggers one but not both triggers
is that a general restriction or do I do something wrong?
I found the error. the insert nedds to be after rahter than before.
Related
I had a mysql trigger that has been working, I exported it and removed it and am trying to put it back, but I keep running into the following 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 '' at line 12
My trigger is:
CREATE TRIGGER `accounts_tracking` AFTER UPDATE ON `accounts`
FOR EACH ROW BEGIN
IF( NEW.`check_level` != OLD.`check_level` ) THEN
INSERT INTO `accounts_tracking` ( `change_type`, `account_id`, `field`, `old_int`, `new_int`, `old_time`, `new_time` )
VALUES
( "1",
OLD.id,
"check_level",
OLD.`check_level`,
NEW.`check_level`,
UNIX_TIMESTAMP(),
UNIX_TIMESTAMP());
END IF;
END
Line #12 is the 2nd UNIX_TIMESTAMP()
My table structure is as follows:
CREATE TABLE `accounts_tracking` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`change_type` smallint(5) unsigned NOT NULL,
`account_id` int(10) unsigned NOT NULL,
`field` varchar(255) NOT NULL,
`old_int` int(11) NOT NULL,
`new_int` int(11) NOT NULL,
`new_time` int(10) unsigned NOT NULL,
`old_time` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `account_id` (`account_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Server type: MySQL
Server version: 5.1.73-log
Thanks.
As barranka suggested in comments section, you need to enclose this trigger in a delimiter, like so:
DELIMITER $$
CREATE TRIGGER `accounts_tracking` AFTER UPDATE ON `accounts`
FOR EACH ROW BEGIN
IF( NEW.`check_level` != OLD.`check_level`) THEN
INSERT INTO `accounts_tracking` ( `change_type`, `account_id`, `field`, `old_int`, `new_int`, `old_time`, `new_time` )
VALUES
( "1",
OLD.id,
"check_level",
OLD.`check_level`,
NEW.`check_level`,
UNIX_TIMESTAMP(),
UNIX_TIMESTAMP());
END IF;
END$$
DELIMITER ;
The reason is that by adding a Begin and End to the statement you are essentially creating a stored routine/procedure with the trigger itself. In order to run multiple statements, like in stored routine/procedure, you need to add delimiters.
In other cases where you do not have the Begin and End within the trigger, you do not need the delimiters. For Example:
CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
CREATE TRIGGER ins_sum BEFORE INSERT ON account FOR EACH ROW SET #sum = #sum + NEW.amount;
I know it is a simple question but i am not finding the answer anywhere.
That's my table:
city | CREATE TABLE `city` (
`ID` int(11) NOT NULL,
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Population` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `FKcode99` (`CountryCode`),
CONSTRAINT `FKcode99` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
The question is: If you want to delete a city, the population must be 275,000 or less.
I tried
ALTER TABLE `world`.`city`
RENAME TO `world`.`DeleteCity` ;
USE `world`;
DELIMITER $$
DROP TRIGGER IF EXISTS world.DeleteCity_ADEL$$
USE `world`$$
CREATE TRIGGER `DeleteCity_ADEL` AFTER DELETE ON `DeleteCity` FOR EACH ROW
IF (OLD.population <= 275000) THEN
DELETE FROM city WHERE NEW.name=OLD.name;
END IF;
$$
DELIMITER ;
but it doesn't work. Can anybody help me?
I would do it in this way:
CREATE TRIGGER `DeleteCity_ADEL` BEFORE DELETE ON `DeleteCity` FOR EACH ROW
IF (OLD.population > 275000) THEN
SIGNAL SQLSTATE '45123'
SET MESSAGE_TEXT = 'If you want to delete a city, the population must be 275,000 or less.';
END IF;
/
This trigger raises an error when somone is trying to delete a record with population > 275,000 and returns a clear explanation (error message) to him.
If population is less or equal than 275,000, the record is silently deleted.
I'll like to find out if it's possible to do the following:
after insertion of data into table a, a row will be created automatically in table b and the Note_Id (its primary key) will be stored in one of the attributes (which is a foreign key that references to the primary key in table b) in table a.
CREATE TABLE table_a ( D_Id int(5) NOT NULL AUTO_INCREMENT,
User_Id int(8) not null,
Note_Id int(5) not null, -- this is the foreign key that points to table b
PRIMARY KEY (D_Id) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE table_b ( Note_Id int(5) NOT NULL AUTO_INCREMENT,
Note_Description varchar(50) null,
PRIMARY KEY (Note_Id) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Thanks!
delimiter $$
CREATE TRIGGER ins_Document
AFTER INSERT ON TABLE_A FOR EACH ROW
BEGIN
set #notenum=(Select max(Note_Id) from TABLE_B);
if(#notenum=0) then begin new.Note_Id=1;
end;
else
new.Note_Id=#notenum+1;
end if;
INSERT INTO TABLE_B (Note_Id) VALUES (NEW.Note_Id);
END$$
delimiter ;
Have a look into triggers: Create Trigger
Here you can react on events like inserts into a table and define respective actions for that.
This is my schema:
I am trying to have an insert into "desktops" or "laptops" insert an id generated automatically from "computers". That works.
My issue is when I insert into either table, I can not select last_insert_id();
Is there something I am doing wrong? I am trying to pass the id all the way forward to my application, for further processing. Selecting MAX(id) is not a valid solution. My SQL connection makes one insert statement, and the trigger should not break that functionality...
Use test;
CREATE TABLE `laptops` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TABLE `desktops` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TABLE `computers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TRIGGER `laptops_BINS` BEFORE INSERT ON `laptops` FOR EACH ROW
BEGIN
IF (EXISTS(SELECT id FROM laptops WHERE name = NEW.name)) THEN
SET NEW.id = NULL;
ELSE
INSERT INTO computers (type) VALUES ('laptop');
SET NEW.id = LAST_INSERT_ID();
SET NEW.id = LAST_INSERT_ID(NEW.id);
END IF;
END
CREATE TRIGGER `desktop_BINS` BEFORE INSERT ON `desktops` FOR EACH ROW
BEGIN
IF (EXISTS(SELECT id FROM desktops WHERE name = NEW.name)) THEN
SET NEW.id = NULL;
ELSE
INSERT INTO computers (type) VALUES ('desktop');
SET NEW.id = LAST_INSERT_ID();
SET NEW.id = LAST_INSERT_ID(NEW.id);
END IF;
END
INSERT INTO laptops (name) VALUES ('laptop1');
INSERT INTO laptops (desktop) VALUES ('desktop1');
INSERT INTO laptops (name) VALUES ('laptop2');
INSERT INTO laptops (desktop) VALUES ('desktop2');
SELECT last_insert_id();
Expecting 4, actually its 0.
Any thoughts as to how I can fix the trigger? Maybe someone can help me format the AFTER_INSERT statement to fix last_insert_id?
I tried setting the values to auto-increment, and unique in the laptops and desktops table, neither will fix the issue.
Rather than trying to deal with the 'confusion' of 'last_insert_id'. I decided to change the table structure to be a more 'common' format.
That is change the 'laptops' and 'desktops' tables to have the 'auto_increment' keys. This changes the 'computers' table to have a primary key of 'computer_id' from 'laptops' or 'desktops' and a 'computer_type'.
Here are the table structures and triggers.
It has been tested on mysql 5.5.16 on windows xp.
CREATE TABLE `laptops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE `desktops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `computers` (
`computer_id` int(11) NOT NULL,
`computer_type` varchar(45) NOT NULL,
PRIMARY KEY (`computer_id`,`computer_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DELIMITER $$
USE `testmysql`$$
DROP TRIGGER /*!50032 IF EXISTS */ `laptop_bins`$$
CREATE
/*!50017 DEFINER = 'test'#'localhost' */
TRIGGER `laptop_bins` AFTER INSERT ON `laptops`
FOR EACH ROW BEGIN
INSERT INTO computers (computer_id, computer_type ) VALUES (new.id, 'laptop');
END;
$$
DELIMITER ;
DELIMITER $$
USE `testmysql`$$
DROP TRIGGER /*!50032 IF EXISTS */ `desktop_bins`$$
CREATE
/*!50017 DEFINER = 'test'#'localhost' */
TRIGGER `desktop_bins` AFTER INSERT ON `desktops`
FOR EACH ROW BEGIN
INSERT INTO computers (computer_id, computer_type ) VALUES (new.id, 'desktop');
END;
$$
DELIMITER ;
Sample Queries and Output:
INSERT INTO laptops (NAME) VALUES ('laptop1');
INSERT INTO desktops (NAME) VALUES ('desktop1');
INSERT INTO laptops (NAME) VALUES ('laptop2');
INSERT INTO desktops (NAME) VALUES ('desktop2');
Laptops:
id name
------ ---------
1 laptop1
2 laptop2
Desktops:
id name
------ ----------
1 desktop1
2 desktop2
Computers:
computer_id computer_type
----------- ---------------
1 desktop
1 laptop
2 desktop
2 laptop
This more a possible approach to the requirement than an answer.
I can create the code if required. It is not a lot of code on top of what is here.
The problem is to maintain tables in an other database, in sync, without doing lots of repeat work.
My suggestion:
In the 'computers' database - have a 'computers_new' table that is inserted to by the 'after insert' trigger and holds the relevant key information. Including a 'unprocessed' column.
I would then run a script at regular intervals or was triggered when the 'computers_new' table changed. It would:
1) transfer the 'unprocessed' details to the 'laptops', 'desktops' tables in the other database.
2) mark the transferred records as processed.
Advantages:
Lots of small chunks of work.
By using transactions it is reliable.
Drawbacks.
Ensuring tables are in sync.
I am trying to perform an INSERT...SELECT to create rows in a 'tasks' table once a row is created in a 'workflow' table. (The process_index used in the workflow creation looks up those tasks required in the 'process_tasks' table then creates them in the tasks table).
The problem, however, is that after performing an insert with the process_index of 'process_one' on the workflow table, the SELECT in the trigger finds no rows. I considered that the #process_id was not being set properly, but with the alternative insert i've commented out in the trigger, it demonstrates that the #process_index is being set correctly. Can anyone advise?
Here is some simplified code to demonstrate the problem:
DROP TABLE IF EXISTS workflow;
CREATE TABLE workflow (
id INT(10) PRIMARY KEY AUTO_INCREMENT,
process_index VARCHAR(12)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS tasks;
CREATE TABLE tasks (
id INT(10) PRIMARY KEY AUTO_INCREMENT,
process_index_used VARCHAR(12),
target_field VARCHAR(12)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS process_tasks;
CREATE TABLE process_tasks (
id INT(10) PRIMARY KEY AUTO_INCREMENT,
process_index VARCHAR(12),
source_field VARCHAR(12)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO process_tasks SET process_index = 'process_one', source_field = 'alpha';
INSERT INTO process_tasks SET process_index = 'process_one', source_field = 'beta';
DROP TRIGGER IF EXISTS workflow_tasks;
DELIMITER //
CREATE TRIGGER workflow_tasks AFTER INSERT ON workflow
FOR EACH ROW BEGIN
DECLARE process_index VARCHAR(12);
SET #process_index = NEW.process_index;
-- INSERT INTO tasks (process_index_used) VALUES (#process_index);
INSERT INTO tasks (target_field) SELECT source_field FROM process_tasks WHERE process_index = #process_index;
END//
DELIMITER ;