I've got a problem with a trigger in MySQL.
My table is the following :
CREATE TABLE IF NOT EXISTS Possessioncartes (
id_carte SMALLINT UNSIGNED,
PRIMARY KEY (id_carte, pseudonyme),
CONSTRAINT fk_inv_carte_id_possession FOREIGN KEY (id_carte) REFERENCES Cartes(id_carte) on delete cascade on update cascade,
pseudonyme VARCHAR(40),
CONSTRAINT fk_inv_carte_pseudo_possession FOREIGN KEY (pseudonyme) REFERENCES Joueurs(pseudonyme) on delete cascade on update cascade,
date_possession DATETIME NOT NULL,
methode_possession VARCHAR(20),
date_non_possession DATETIME,
etat SMALLINT UNSIGNED DEFAULT 1 NOT NULL
);
and my trigger :
create trigger posseder after insert on Possessioncartes
for each row begin
set #date_non_possession = new.date_possession
where #id_carte = new.id_carte;
end$$
( When I add a new card to the table, the trigger should update all the rows that has the same ID )
when I tried to add a new row, I got no errors but no row was updated.
I tried omitting the # but I got an "unknown system variable" error.
Thanks for your time.
Triggers cannot modify the table they are "ON", nor any table involved in the query that triggered them.
For example an update trigger on A, cannot modify B for an update statement like UPDATE A INNER JOIN B ON something SET A.x = somethingelse....
Related
I have a table called 'estoque' with a foreign key that references another table called 'produto'. Then I've populated both tables with a few rows.
Here are my tables:
CREATE TABLE `produto` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`NOME` varchar(45) NOT NULL,
`PRECO` float NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `NOME_UNIQUE` (`NOME`)
);
CREATE TABLE `estoque` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ID_PRODUTO` int(11) NOT NULL,
`QUANTIDADE_PRODUTO` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `fk_Estoque_Produto1_idx` (`ID_PRODUTO`),
CONSTRAINT `fk_Estoque_Produto1` FOREIGN KEY (`ID_PRODUTO`)
REFERENCES `produto` (`ID`)
);
I need 'estoque' to always reference all existing rows on 'product'. So I'e created an AFTER INSERT and an AFTER UPDATE triggers on product:
CREATE TRIGGER `cadastrar_novo_produto_no_estoque`
AFTER INSERT ON `produto`
FOR EACH ROW
INSERT IGNORE INTO estoque (ID_PRODUTO)
VALUES (NEW.ID);
EDIT: Actually, since I can't alter the 'ID' column on 'produto' because it is a primary key, I've think I don't need the AFTER UPDATE trigger at all. Am I right?
CREATE TRIGGER `atualizar_novo_produto_no_estoque`
AFTER UPDATE ON `produto`
FOR EACH ROW
UPDATE estoque
SET estoque.ID_PRODUTO = NEW.ID
WHERE OLD.estoque.ID_PRODUTO = OLD.ID;
Now I need a trigger so that every time I delete a row from 'product', it also deletes the corresponding row in 'estoque'.
I've tried creating one like this:
CREATE TRIGGER `deletar_produto_inexistente_no_estoque`
BEFORE DELETE ON `produto`
FOR EACH ROW DELETE FROM estoque
WHERE estoque.ID_PRODUTO = ID;
But whenever I try to delete a row from 'produto' I get the following error:
ERROR 1175: 1175: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
SQL Statement:
DELETE FROM `papelaria`.`produto` WHERE (`ID` = '6')
So I've tried it with the OLD keyword, as such:
CREATE TRIGGER `deletar_produto_inexistente_no_estoque`
BEFORE DELETE ON `produto`
FOR EACH ROW DELETE FROM estoque
WHERE OLD.estoque.ID_PRODUTO = OLD.ID;
And then I get this error instead:
ERROR 1054: 1054: Unknown column 'OLD.estoque.ID_PRODUTO' in 'where clause'
SQL Statement:
DELETE FROM `papelaria`.`produto` WHERE (`ID` = '6')
What am I missing or doing wrong?
P.S.: Not sure if it's worth mentioning but I'm fairly new to sql and programming in general, so I'd appreciate it if you took it into consideration when answering (for all purposes, just assume I don't know anything about anything ^^)
Thank you in advance!
You can modify your FK to:
CONSTRAINT `fk_Estoque_Produto1`
FOREIGN KEY (`ID_PRODUTO`)
REFERENCES `produto` (`ID`)
ON DELETE CASCADE
Then you will not need the before delete trigger any longer as records in estoque table will be deleted automatically.
Similar, adding ON UPDATE CASCADE will solve your 'update issue' in case someone update record's PK.
Still not sure why you'd like to have the dummy records in the estoque table.
I have two tables in relation 1:n
create table A (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
data VARCHAR(32),
data2 INTEGER,
INDEX (id)
);
CREATE TABLE B (
A_id BINGINT,
some_data DOUBLE,
INDEX (A_id),
FOREIGN KEY (A_id) REFERENCES A(id) ON DELETE CASCADE
);
If i update a row in table A then i need to delete all records from table B with same id from table A. What is the best way to solve it?
i dont know if i should use a trigger or an event.
You need a trigger on table A:
DELIMITER //
CREATE TRIGGER t BEFORE UPDATE ON A
FOR EACH ROW
BEGIN
DELETE FROM B WHERE a_id = NEW.id;
END
//
DELIMITER ;
Lets say I have 2 tables:
DROP TABLE IF EXISTS `datasetCalculation`;
DROP TABLE IF EXISTS `dataset`;
CREATE TABLE IF NOT EXISTS `dataset`(
`datasetID` INT NOT NULL AUTO_INCREMENT,
`datasetWeekOf` DATE, #date indicates the beginning of a week
`dataInsertedByID` INT NOT NULL,
`datasetItem1` INT,
`datasetItem2` INT,
`datasetItem3` INT,
PRIMARY KEY (`datasetID`, `datasetWeekOf`)
)Engine = InnoDB;
CREATE TABLE IF NOT EXISTS `datasetCalculation`(
`datasetID` INT NOT NULL,
`datasetWeekOf` DATE,
`datasetItemSum` INT,
PRIMARY KEY (`datasetID`, `datasetWeekOf`),
CONSTRAINT `datasetID_FK`
FOREIGN KEY (`datasetID`)
REFERENCES `dataset` (`datasetID`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `datasetWeekOf_FK`
FOREIGN KEY (`datasetID`)
REFERENCES `dataset` (`datasetID`)
ON UPDATE CASCADE
ON DELETE CASCADE
) Engine = InnoDB;
What I'd like to do is when dataset is updated with the next week's information (it is updated once a week), automatically insert the relevant information into datasetCalculation. The equivalent would be to calculate in some language (let's say php) the sum of datasetItem(1-3), then inserting that into datasetCalculation.datasetItemSum with the other relevant information. Instead I'd like this to be done automatically by the database.
I would like to update multiple rows of column 'student_total' in Table 'Teacher' when a student is deleted, using triggers/procedures
Updating multiple rows in related tables, many-to-many realtionships
'n' teachers can have 'm' students
is it possible at all ? because its not possible to store a
result set (learnt from your site)
in Mysql, Postgress etc?
Thanks in Adv
Ritin
--------------- SQL
CREATE TABLE `Teacher` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`student_total` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
);
CREATE TABLE `Student` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `Teacher_has_Student` (
`teacher_id` smallint(5) unsigned NOT NULL,
`student_id` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`teacher_id`,`student_id`),
KEY `fk_Teacher_has_Student_teacher` (`teacher_id`),
KEY `fk_breeder_has_breed_student` (`student_id`),
CONSTRAINT `fk_Teacher_has_Student_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `Teacher` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_breeder_has_breed_student` FOREIGN KEY (`student_id`) REFERENCES `Student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
i have triggers for INSERT/DELETE/UPDATE
ex:
---------------------- SQL
CREATE TRIGGER teacher__student_insert AFTER INSERT ON Teacher_has_Student
FOR EACH ROW
BEGIN
UPDATE Teacher SET student_total = student_total + 1 WHERE id = NEW.teacher_id;
END;
The trigger below updates just 1 row, whereas the aim is to update all the rows.
DELIMITER |
CREATE TRIGGER my_student__delete AFTER DELETE ON Student
FOR EACH ROW
BEGIN
set #std_id = old.id;
UPDATE teacher SET student_total = student_total - 1
WHERE id = #std_id;
END
|
I think your 2nd trigger doesn't make sense. On a delete of a student you take his id and decrease the student_total of the teacher, which has accidentally the same id as the as the studend.
Your definition plus the first trigger should already do the job. If you delete a student his relations are deleted from Teacher_has_Student by the CASCADE and on this DELETE your triggers should be fired to decrease the student_total of the affected teachers. Anyway a short Google research shows, that MySQL apparently doesn't fire triggers on a cascaded delete:
http://bugs.mysql.com/bug.php?id=13102
Possible Workarounds
You could try to fix your 2nd trigger to do the job manually:
DELIMITER |
CREATE TRIGGER my_student__delete AFTER DELETE ON Student
FOR EACH ROW
BEGIN
SET #std_id = old.`id`;
UPDATE
`Teacher` AS T
INNER JOIN `Teacher_has_Student` AS TS ON T.`id`=TS.`teacher_id`
SET `student_total` = `student_total` - 1
WHERE ST.`student_id` = #std_id;
END
|
But this will not work, if the CASCADE on the FOREIGN KEY is executed before your trigger.
Maybe a more reliable workaround is to drop the FOREIGN KEY definitions and to create triggers on Teacher and Student to cascade the deletes and updates to Teacher_has_Student.
Normalize it
With the workarounds you have to keep a lot of different scenarios in mind to keep student_total valid. If it‘s not absolutely necessary to store student_total directly at the Teachers-Table, I suggest strongly not to do it.
The information is absolutely redundant. Keep your constraints and cascades, but drop the student_total column and delete all triggers. Then use a JOIN to get your student numbers:
SELECT
T.`id`, T.`name`, COUNT(*) AS student_total
FROM `Teacher` AS T
INNER JOIN `Teacher_has_Student` AS TS ON T.`id`=TS.`teacher_id`
GROUP BY T.`id`
I am developing a MySQL database using Workbench. I want two send two fields from a newly created record to another table. I would then like to update the original table with newly created data from the second table. I was looking to implement this with triggers, unless there is a better way of course :) My attempt was a fail when I went to upload it(see below)
Specifically, I would like tc_Event to send the ID & tc_EventTags_ID to tc_EventTags to fill in tc_Tag_ID & tc_Event_ID. Afterwards I want the ID of tc_EventTags sent back to tc_Event to the tc_EventTags_ID field.
Thanks for any help.
-- -----------------------------------------------------
-- Table `mcontest`.`tc_EventTags`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mcontest`.`tc_EventTags` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`tc_Tag_ID` INT NOT NULL ,
`tc_Event_ID` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_tc_EventTags_tc_Tag1` (`tc_Tag_ID` ASC) ,
INDEX `fk_tc_EventTags_tc_Event1` (`tc_Event_ID` ASC) ,
CONSTRAINT `fk_tc_EventTags_tc_Tag1`
FOREIGN KEY (`tc_Tag_ID` )
REFERENCES `mcontest`.`tc_Tag` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_EventTags_tc_Event1`
FOREIGN KEY (`tc_Event_ID` )
REFERENCES `mcontest`.`tc_Event` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = MyISAM;
-- -----------------------------------------------------
-- Table `mcontest`.`tc_Event`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mcontest`.`tc_Event` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`date` DATE NOT NULL ,
`time` TIME NOT NULL ,
`location` VARCHAR(45) NOT NULL ,
`description` VARCHAR(45) NOT NULL ,
`tc_EventTags_ID` INT NULL ,
`tc_Orgs_ID` INT NOT NULL ,
`tc_PersonEvent_ID` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_tc_Event_tc_EventTags1` (`tc_EventTags_ID` ASC) ,
INDEX `fk_tc_Event_tc_Orgs1` (`tc_Orgs_ID` ASC) ,
INDEX `fk_tc_Event_tc_PersonEvent1` (`tc_PersonEvent_ID` ASC) ,
CONSTRAINT `fk_tc_Event_tc_EventTags1`
FOREIGN KEY (`tc_EventTags_ID` )
REFERENCES `mcontest`.`tc_EventTags` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_Event_tc_Orgs1`
FOREIGN KEY (`tc_Orgs_ID` )
REFERENCES `mcontest`.`tc_Orgs` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_Event_tc_PersonEvent1`
FOREIGN KEY (`tc_PersonEvent_ID` )
REFERENCES `mcontest`.`tc_PersonEvent` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = MyISAM;
USE `mcontest`;
DELIMITER $$
USE `mcontest`$$
CREATE TRIGGER eventTag_Trigger
AFTER insert ON tc_Event
FOR EACH ROW BEGIN
INSERT INTO tc_EventTags values('',NEW.tc_Event_ID);
END;
END$$
DELIMITER ;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
Why have put a tc_EventTags_ID in table tc_Event? What is the logic behind that?
I mean that the relationship between the 2 tables would be (I guess): 1 Event - many EventTags. This is already achieved by the tc_EventTags.tc_Event_ID which is a Foreign Key to tc_Event.
To answer your question:
As it is now, the Trigger tries for every row inserted in table Event, to add a row in table EventTag. But it will fail for 2 reasons:
EventTag has 2 Constraints (Foreign keys) which have to be fulfilled for the triggered insert to succeed. So, tc_Event_ID is ok but tc_Tag_ID has to be NOT NULL and reference the Tag table but the value you supply, '', probably is not in table Tag.
EDIT: the value you supply, '', is also a CHAR while it should be INT.
But trigger will probably not be started at all, since every time you insert a row in table Event, those constraints have to be fulfilled and one of them is the tc_EventTags_ID which references EventTag table. But EventTag table is empty. Do you see the circular logic here?