Error in mysql syntax: Befor Update Trigger - mysql

I am setting up a master master replication and trying to do some tests by setting up a before update trigger.
I am getting an error when I run the code below
CREATE TRIGGER update_blogs
BEFORE UPDATE ON blogs
FOR EACH ROW
BEGIN
IF (NEW.updated_replication < OLD.updated_replication) THEN
SET NEW= OLD ;
END IF;
END$$
ERROR 1064 (42000): 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 6
What I am trying to do is only allow the row to be updated if the new row has a greater updated_replication(timestamp) value.
I am using mysql.
Can any one please tell me where I am wrong. How can I debug such errors? Is this any kind of syntax error?

Two problems:
First problem: you can't SET NEW = OLD. You can only assign individual columns, not the whole row. So you could make sure the new value does not decrease:
IF (NEW.updated_replication < OLD.updated_replication) THEN
SET NEW.updated_replication = OLD.updated_replication;
END IF;
But that will set one column and let any other columns change according to the UPDATE that spawned this trigger. That might leave you with data that doesn't agree with itself.
If you want the whole row to revert to the old column values, you'd have to write a series of assignments in the SET statement, one for each column of the row.
If you instead want to abort the whole update, then you need to learn the SIGNAL feature.
IF (NEW.updated_replication < OLD.updated_replication) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'You can\'t travel backwards in time!';
END IF;
This doesn't roll back the transaction, just the single UPDATE that spawned the trigger. Any other changes made in the same transaction are still pending.
Second problem: you haven't set the DELIMITER when defining a trigger with a compound statement. See my answer here: Create function through MySQLdb

I don't think assignment of a database record with tableA = tableB is legal which is essentially what NEW=OLD is doing.
It would be better to do some other operation if the update is not desired:
CREATE TRIGGER update_blogs
BEFORE UPDATE ON blogs
FOR EACH ROW
BEGIN
IF (NEW.updated_replication >= OLD.updated_replication) THEN
SET NEW.invalid = True;
END IF;
END;$$
Where invalid is a column added just for this purpose. A periodically run stored procedure could then deal with such records, perhaps by deleting them.

Related

Keep getting error #1064 when I try to create a trigger in phpmyadmin

I'm trying to create a trigger to import a field from another table when a new row is inserted but get the error message:
#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' at line 6
I'm using the sql tab in phpmyadmin to enter the code.
I've read all the related questions on here, perused several other sites with articles on mysql triggers but can't find anything that helps.
I've tried with // at the end of the line prior to END//
I've tried with ; at the end of the line prior to END//
Nothing seems to work.
My trigger code:
DELIMITER //
CREATE TRIGGER towns2_before_insert
BEFORE INSERT ON towns2 FOR EACH ROW
BEGIN
SET NEW.regionid = (Select RegionID from world_map
where (world_map.X = new.mapx) AND (world_map.Y = new.mapy))
END//
DELIMITER ;
Both towns2 and world_map tables exist and have the required columns.
As far as I can tell this should fetch the RegionID field from any row in the world_map table that has X & Y fields matching the new mapx and mapy columns but all I can get is this error.
No doubt I've got a really simple error in this somewhere but for the life of me I can't find it and all help is greatfully recieved.
UPDATE
Re-created the code as shown by #Chris J below in the myphpadmin trigger form, ignored the big red X against the END statement and hit the GO button - it worked so I guess the problem was not with my code but with my chosen method of creating the trigger. The code as created by phpmyadmin is shown below:
CREATE TRIGGER `towns2_before_insert` BEFORE INSERT ON `towns2`
FOR EACH ROW BEGIN
SET NEW.regionid =
(Select RegionID from world_map
where (X = new.mapx) AND (Y = new.mapy));
END
Although you've mentioned it already, the following works perfectly for me:
DELIMITER //
CREATE TRIGGER towns2_before_insert
BEFORE INSERT ON towns2 FOR EACH ROW
BEGIN
SET NEW.regionid = (Select RegionID from world_map
where (world_map.X = new.mapx) AND (world_map.Y = new.mapy));
END//
This is based on a guess of your schema from your query. The trigger now exists and is visible in Heidi as follows:

SQL where syntax error with trigger

create trigger cal_retweet before insert on T
for each row begin
set NEW.retweet_change = NEW.retweet_count - retweet_count where id_str = NEW.id_str
end
SQL said there is syntax error near "where id_str = NEW.id_str"
My table looks like this. Where id_str is a unique identifier for a specific tweet. Since I am inserting 50 tweets from a single user every minute, there would be many same id_str. What I want to look at is the change of retweet_count every minute. tweeted_at is when the user tweeted, created_at is when this data is inserted into my database. I want to generate retweet_change for each new data inserted into the database compared to the same old tweet (into the column retweet_change). How should I write the trigger?
After reading some of your comments I changed my code to :
create trigger cal_retweet before update on T
for each row
begin
set NEW.retweet_change = NEW.retweet_count - OLD.retweet_count;
end;
There is still syntax error
There are several issues with this trigger.
You have some syntax errors. You need proper semicolons to delimit your statements.
You have a WHERE statement that is out of place (and actually not needed). You are acting on only a single row at a time, you don't have to match on the id_str.
In order to factor in a calculation using an existing value from the row, you need access to the OLD keyword. For that, you need a trigger that happens on UPDATE, not INSERT. On INSERT, the retweet_change is simply the same as retweet_count; you could alter your INSERT statement to fix that problem.
You may need to explicitly add a statement delimiter as per the comments below.
So all together, I think this trigger should look like:
DELIMITER //
CREATE TRIGGER cal_retweet BEFORE UPDATE ON T
FOR EACH ROW
BEGIN
SET NEW.retweet_change = NEW.retweet_count - OLD.retweet_count;
END;//
DELIMITER ;

MySQL Trigger - Don't accept record update if outside in range

I'm trying to create (what I think) should be a basic trigger.
Essentially if someone tries putting a value in for a record that is outside the range of the trigger, then it would refuse the update.
Table is called: People
Field concerned is: age
CREATE TRIGGER max_age_trigger
BEFORE UPDATE
ON People
FOR EACH ROW
BEGIN
IF People.age <0 OR People.age>150 THEN
CALL 'Error: The age is out of range (0 > 150)';
END IF;
END
MySQL is throwing an error at line 7. However i'm not understanding where i'm going wrong. I'm new to triggers and still getting my head around it.
If you could help/assist me in my code, that would be of great help :)
If you want to throw an error and stop processing, use SIGNAL:
CREATE TRIGGER max_age_trigger BEFORE UPDATE
ON People
FOR EACH ROW
BEGIN
IF People.age <0 OR People.age>150 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Error: The age is out of range (0 > 150)';
END IF;
END

Trigger only if Table if updated

I want to trigger the table after update only if any change is made in the table.
I tried this
but it is giving error.
My code is
CREATE TRIGGER Trans_SubCategory_update AFTER UPDATE ON Trans_SubCategory
FOR EACH ROW
BEGIN
IF NEW.ts <> OLD.ts THEN
INSERT INTO Sync_activities (table_name,table_id,admin_id,action)
VALUES('Trans_SubCategory',New.id,New.admin_id,'update');
END IF;
END;
It is giving error
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 6 .
But if I use
CREATE TRIGGER Trans_SubCategory_update AFTER UPDATE ON Trans_SubCategory
FOR EACH ROW
INSERT INTO Sync_activities (table_name,table_id,admin_id,action)
VALUES('Trans_SubCategory',New.id,New.admin_id,'update');
I add the trigger but triggers even if no change is made in table.
The thing is - you're forgetting to set proper delimiter. Your first syntax contains multiple operators inside trigger - and they need to be delimited by ; - but you're not changing global delimiter - thus, MySQL treats that as syntax end - and, therefore, that's an error (because trigger isn't completed, obviously).
Your second syntax is executed completely since it has only one operator (and that stands for trigger end as well) - you've not enclosed it by BEGIN..END
To fix the issue, just do:
DELIMITER //
CREATE TRIGGER Trans_SubCategory_update AFTER UPDATE ON Trans_SubCategory
FOR EACH ROW
BEGIN
IF NEW.ts != OLD.ts THEN
INSERT INTO Sync_activities (table_name,table_id,admin_id,action) VALUES ('Trans_SubCategory', NEW.id, NEW.admin_id,'update');
END IF;
END;//
DELIMITER ;

Protect column, disallow update, only allow insert if NULL in MySQL

I want to protect existing dates in a date column from being overwritten. So disallow updates to the date column and only allow inserts if the existing field value is NULL (date column default is NULL). Are triggers the only way to accomplish this in MySQL? If so, would the trigger below work?
create trigger date_check
before insert, update on date
for each row
begin
if(date IS NOT NULL) then
SIGNAL 'date already set'
end if ;
end ;
Background: I have a table with critical dates that was accidentally changed due to user error. I put some checks in the user interface to prevent this from happening again but want another layer of safety directly with the database if possible.
Yes, in MySQL triggers are the only way to do this. MySQL does not support constraints.
Your trigger is not exactly right. First, you have update on date, but this should be update on <table name>. Second, you are checking the date value used for the update. Perhaps you mean:
create trigger date_check_update
before update on <the table name goes here>
for each row
begin
if (old.date IS NOT NULL) then
SIGNAL 'date already set'
end if ;
end;
An insert trigger on this condition doesn't make sense.
If anyone like me stumble upon this thread and is getting syntax error, it's because "When you try to raise errors via SIGNAL you need to specify the SQLSTATE which is the error code and for the user defined generic error codes its 45000 along with the message text MESSAGE_TEXT"
So the SIGNAL line should look like this.
signal SQLSTATE VALUE '45000' SET MESSAGE_TEXT = 'Your custom error message';
See this answer for more details.
https://stackoverflow.com/a/42827275/4164651
Just combining the above two answers, however, if you are writing triggers directly at the terminal, you'll have to change the delimiter before writing the trigger and then change it back once done.
delimiter $$
create trigger date_check_update
before update on <the table name goes here>
for each row
begin
if (old.date IS NOT NULL) then
signal SQLSTATE VALUE '45000' SET MESSAGE_TEXT = 'Your custom error message';
end if ;
end $$
delimiter ;