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 ;
As illustrated in the image below, I want the column grand_score to be automatically calculated when values for paper_score and final_score are inserted.
This is what i have tried:
DELIMITER $$
CREATE TRIGGER myTableAutoSum
BEFORE INSERT ON `stumgr_scores` FOR EACH ROW
BEGIN
SET NEW.grand_score= NEW.paper_score + NEW.final_score;
END;
$$
DELIMITER ;
Issue
when i input values for paper_score and final_score, the table does not get updated (i mean grand_score)
Image
I don't immediately see what is wrong with your trigger, unless one of the values happens to be NULL. That is easily fixed using COALESCE():
SET NEW.grand_score = COALESCE(NEW.paper_score, 0) + COALESCE(NEW.final_score, 0);
I question the use of triggers for this purpose. You also need a trigger for UPDATE, in case the values change that way.
Isn't simpler to remove grand_total from the table and use a view?
create view v_stumgr_scores as
select ss.*,
COALESCE(ss.paper_score, 0) + COALESCE(ss.final_score, 0) as grand_score
from stumgr_scores ss;
Or, better yet, in the most recent versions of MySQL, just use a generated column. That is the best solution -- it even allows you to create an index on the value.
Is it possible to pass the NEW and the OLD tables from a trigger into a procedure in MySQL?
I suspect no, since there is no such a datatype as table that a procedure accepts.
Any workarounds possible?
Ideally it would look like this:
CREATE TRIGGER Product_log AFTER UPDATE ON Product
FOR EACH ROW BEGIN
call logChanges(OLD, NEW);
END;
You can explicitly pass each field:
CALL logChanges(OLD.colA, OLD.colB, NEW.colA, NEW.colB);
Or if logChanges must be sufficiently generic that it can handle such calls from different tables, one could concatenate the field values into a single string using a suitable delimiter (e.g. the unit separator):
CALL logChanges(CONCAT_WS(CHAR(31), OLD.colA, old.colB),
CONCAT_WS(CHAR(31), NEW.colA, NEW.colB));
Or if data types must be preserved, one could insert the records into a temporary from which logChanges reads.
it's not possible because there is no NEW or OLD table. The entire trigger is related to the table - the "new" and "old" refer to the rows and the values they contained before and after the event that was triggered. In other words, your example would be:
call logChanges(OLD.customername, NEW.customername)
You could also save all the OLD data in a history table (which I expect logchanges does anyways), basically being a clone of the production table something like this:
BEGIN
IF OLD.customer_name != NEW.customer_name
THEN
INSERT INTO myTable_chagne_history
(
customer_id ,
customer_name ,
another_field ,
edit_time
)
VALUES
(
OLD.customer_id,
OLD.customer_name,
OLD.another_field ,
NEW.time_edit_was_made
);
END IF;
END;
I am seeking a short term solution while I work out why a synchronisation is setting one field wrong in a table
I prepared a trigger and would welcome some comment on it, and any necessary corrections or better strategies.
CREATE TRIGGER urlcorrect AFTER INSERT ON sym_node
FOR EACH ROW BEGIN
IF NEW.sync_url= 'http://wrongaddress' THEN
UPDATE sym_node SET sync_url= "http://123.456.7.89:1234/etc";
END IF;;
END$
delimiter;
thanks
David
Your trigger is wrong in various ways.
First of all, I think you want a BEFORE trigger so that you can fix the row before it gets into your table.
Secondly, this:
UPDATE sym_node SET sync_url= "http://123.456.7.89:1234/etc";
would update every sync_url in the sym_node table and that's not what you want. And I don't think MySQL will let you UPDATE a table inside a trigger on that table (someone correct me if I'm wrong on this please). Also, you should be using single quotes for string literals even though MySQL will let you use double quotes, don't pick up bad habits from MySQL lax behavior. You want to:
set new.sync_url = 'http://123.456.7.89:1234/etc';
Putting all that together, you get this:
delimiter $
create trigger urlcorrect before insert on sym_node
for each row begin
if new.sync_url = 'http://wrongaddress' then
set new.sync_url = 'http://123.456.7.89:1234/etc';
end if;
end;
$
delimiter ;
I'm using in my database, many fields of a certain range, like:
CREATE TABLE figures (
deg FLOAT,-- between 0 and pi
prc FLOAT,-- between 0 and 1
.......
);
CREATE TRIGGER filter1 BEFORE UPDATE ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, OLD.deg,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, OLD.prc,NEW.prc),
..........;
CREATE TRIGGER filter2 BEFORE INSERT ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, NULL,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, NULL,NEW.prc),
.........;
Is there any way to write it more clearly ?
Something like:
--CREATE PROCEDURE/FUNCTION between()..................
CREATE TABLE figures (
deg FLOAT between(0,3.1415),
prc FLOAT between(0,1),
.......
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
prior to MySQL 8.0.16
Triggers are the best solution
Re:check constraints...
'The CHECK clause is parsed but ignored by all storage engines.'.....
'The reason for accepting but ignoring syntax clauses is for compatibility, to
make it easier to port code from other SQL servers, and to run applications
that create tables with references. '
lifted directly from: http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
From MySQL 8.0.16 though they now work as you would expect
CREATE TABLE figures (
deg FLOAT,
prc FLOAT,
CONSTRAINT `deg_min` CHECK ((`deg` > 0)),
CONSTRAINT `deg_max` CHECK ((`deg` < 3.1415)),
CONSTRAINT `prc_min` CHECK ((`prc` > 0)),
CONSTRAINT `prc_max` CHECK ((`prc` < 1))
)
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
You can write a stored function and call that in your trigger.
DELIMITER $$
CREATE FUNCTION check_deg (degree FLOAT, olddegree FLOAT) RETURNS FLOAT
BEGIN
DECLARE result FLOAT;
result = IF(degree>3.1415926 OR degree <0, olddegree,degree);
RETURN result;
END$$
DELIMITER ;
That way you have one point where the limits are defined and if anything changes you only have to change the boundaries in one place.
The best solution is to use a CHECK() constraint, but MySQL doesn't support CHECK() constraints. (MySQL parses them, then ignores them.)
In some cases, you can replace a CHECK() constraint with a foreign key reference to a table that contains all the valid values. Floating-point numbers are not a good candidate for that kind of solution, though.
That leaves triggers. In your case, your best bet is to use a trigger that calls a stored function.
I have a unfinished idea :
CREATE PROCEDURE check_constraint (table VARCHAR,field VARCHAR,condition VARCHAR)
BEGIN
SET #update_trigger = CONCAT ("
IF EXIST TRIGGER check_constraint_",table,"_",field,"
BEGIN /*I don't know yet what to do*/ END
ELSE /*IF TRIGGER DONT EXIST*/
BEGIN
CREATE TRIGGER check_constraint_",table,"_",field,"
BEFORE UPDATE ON ",table," FOR EACH ROW SET
NEW.",field,"=IF("condition, ", OLD.",field,",NEW.",field,");
END
");
PREPARE update_trigger FROM #update_trigger;
EXECUTE update_trigger;
DEALLOCATE PREPARE update_trigger;
SET #insert_trigger = ..............................
END
After we have a completed function, we can just call it during creation of the database:
CALL check_constraint("table","field","NEW.field<34567");