This is oracle trigger :
CREATE OR REPLACE TRIGGER "SW_TARIFF_TARIFF_TRIGGER"
BEFORE INSERT OR UPDATE OF "LAST_UPDATED_DT" ON "DEMO_DB"."SOLID_WASTE_TARIFF_MASTER"
REFERENCING OLD AS "OLD" NEW AS "NEW"
FOR EACH ROW
BEGIN
:NEW.LAST_UPDATED_DT := SYSDATE;
END;
I want to move it to mysql. But i don't know know i write cretae and replace with one trigger and also have problem with mysql replacement for of keyword of oracle . Can any one suggest me how to convert this oracle trigger to mysql .
In MySql you need to create two separate triggers, a first trigger BEFORE INSERT and a second trigger BEFORE UPDATE.
MySql doesn't support this clause UPDATE OF column_name - that is the trigger is fired only when a change of the specified column occured. In MySql the trigger is always fired, and You need to detect this condition yourself in a body of the trugger.Try:
CREATE TRIGGER SW_TARIFF_TARIFF_TRIGGER1
BEFORE INSERT ON SOLID_WASTE_TARIFF_MASTER
FOR EACH ROW
SET NEW.LAST_UPDATED_DT := Now();
and this:
CREATE TRIGGER SW_TARIFF_TARIFF_TRIGGER2
BEFORE UPDATE ON SOLID_WASTE_TARIFF_MASTER
FOR EACH ROW
BEGIN
IF NEW.LAST_UPDATED_DT <> OLD.LAST_UPDATED_DT
AND NEW.LAST_UPDATED_DT IS NOT NULL
AND OLD.LAST_UPDATED_DT IS NOT NULL
OR NEW.LAST_UPDATED_DT IS NULL AND OLD.LAST_UPDATED_DT IS NOT NULL
OR NEW.LAST_UPDATED_DT IS NOT NULL AND OLD.LAST_UPDATED_DT IS NULL
THEN
SET NEW.LAST_UPDATED_DT := Now();
END IF;
END;
This horrible IF condition detects if the column LAST_UPDATED has been changed (it simulates UPDATE OF "LAST_UPDATED_DT" clause from Oracle)
Please refer to the documentation for more information about triggers in MySql:
http://dev.mysql.com/doc/refman/5.7/en/create-trigger.html
https://dev.mysql.com/doc/refman/5.5/en/trigger-syntax.html
Related
I am writing my first stored procedure as a trigger. I am doing this in a dev migration as we have two systems which don't speak to each other in dev, so I need to mock the data which would normally come from the other system.
My procedure is added as part of our dev migration script.
DELIMITER |;
CREATE TRIGGER `activity_insert` AFTER INSERT ON `activity`
FOR EACH ROW
BEGIN
UPDATE `activity` AS `a` JOIN `handle` AS `h` on `a.handle_id` = `h.handle_id` SET `path` = CONCAT(`h.handle`,'/',`a.activity_handle`) WHERE `a.path` IS NULL;
END;
|
DELIMITER;
I would expect the logic to be:
DELIMITER $$
CREATE TRIGGER activity_insert BEFORE INSERT ON activity
FOR EACH ROW
BEGIN
IF new.path IS NULL THEN
SET new.path = (SELECT CONCAT(h.handle, '/', new.activity_handle)
FROM handle h
WHERE new.handle_id = h.handle_id
);
END IF;
END;$$
DELIMITER;
There are numerous problem with your code:
You don't update the table being modified using update.
You want a "before" triggers, not an "after trigger".
Don't use | for the the delimited. It is a valid MySQL operator.
You have over-used the backtick, including putting the table alias in with the column alias.
This assumes that handle.handle_id is unique. This seems like a reasonable assumption based on the names, but you can add limit 1 to guarantee no more than one row is returned.
What is the postgres equivalent of the below mysql code
CREATE TABLE t1 (
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE t2 (
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
As per Alex Brasetvik answer below, it seems i should go with triggers, my problem is i have a number of tables t1, t2... with created and modified fields, is it possible to write a generalized procedure?
--update
Almost ready
CREATE FUNCTION update_timestamp() RETURNS trigger AS $update_timestamp$
BEGIN
NEW.modified := current_timestamp;
RETURN NEW;
END;
$update_timestamp$ LANGUAGE plpgsql;
CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON t1
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON t2
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
Just make sure all tables have the same columnname:
CREATE OR REPLACE FUNCTION upd_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
CREATE TRIGGER t_name
BEFORE UPDATE
ON tablename
FOR EACH ROW
EXECUTE PROCEDURE upd_timestamp();
Thank you for the information Mithun and Alex Brasetvik.
I'd like to add one minor tweak to the trigger. Since we mostly likely want the modified column to store the timestamp when the row was last changed, not when it was the target of an UPDATE statement, we have to compare the new and the old value of the row. We update the modified column only if these two values differ.
CREATE OR REPLACE FUNCTION update_modified_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
IF (NEW != OLD) THEN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END IF;
RETURN OLD;
END;
$$;
This trigger ensures that the modified column is updated only if the UPDATE operation actually changes the values stored in the row.
Update it with a trigger. Documentation and examples.
To further improve, the answer given by #Lauri Silvennoinen:
This trigger uses the WHEN clause as recommended by the official docs to check for changes in the row even before calling the specified function.
CREATE OR REPLACE FUNCTION update_modified_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
CREATE OR REPLACE TRIGGER tg_update_modified
AFTER UPDATE ON table_name
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE FUNCTION update_modified_timestamp();
I'd like to make a TIMESTAMP field DEFAULT CURRENT_TIMESTAMP, for 'creation time' purpose. But if someone or somehow something changes that TIMESTAMP, my data won't be consistent.
Is there a way I can ensure it won't change unless I delete the row and reinsert it, other than application level?
With the suggested answer provided, i could work around with something like this
CREATE TRIGGER consistency1 BEFORE UPDATE ON table1
FOR EACH ROW
BEGIN
IF NEW.creationtime != OLD.creationtime THEN
SET NEW.creationtime = OLD.creationtime;
END IF;
END;
Since my comment has been appreciated, here's the extended version.
I personally don't think that it's possible.
Anyway, there are a couple of things you can try:
Make sure that only your application can write on the database
Write a trigger like this (pseudocode!)
create trigger prevent_change_timestamp on tbl_name
before update
#fetch old row value
#verify if the timestamp field has been changed
#raise an error (any SQL error will do)
Or like this
create trigger revert_change_timestamp on tbl_name
after update
#fetch pre-change row value
#update the row with the "old" value in place of the new one
I'd personally go with the 3rd option, if possible. Anyway, the 2nd one is good too. I'd not rely on the 1st option unless necessary (eg: no access to trigger functionality)
More info here: reference
It's funny in a way that database apps don't offer this functionality as standard: not only for a "created" timestamp field, but for things like autoincrement id fields, and any miscellaneous values which you may want to set on creating a record and then never allow to be changed... wonder what the rationale is?
What you can do here is, you can write a TRIGGER on the table when a row is being updated. In that trigger, you can compare the old and new values, and if they are different then you can just overwrite the new value with the old one.
I tried this in MySQL 5.1 and got an error
DELIMITER //
CREATE TRIGGER member_update_0
-> AFTER UPDATE ON members
-> FOR EACH ROW
-> BEGIN
-> IF NEW.id != OLD.id THEN
-> SET NEW.id = OLD.id;
-> END IF;
-> END;//
ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger
The same trigger with AFTER replaced by BEFORE is accepted;
to me, this is a counter-intuitive way to do it, but it works
delimiter ;
UPDATE members SET id=11353 WHERE id=1353;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
It is actually possible to do this very neatly if you are using InnoDB.
Create another table with just one column. That column should have a foreign key (hence the innodb requirement in this solution) that points to the immutable column of the original table in question.
Put a restriction like "ON UPDATE RESTRICT".
In summary:
CREATE TABLE original (
....
immutable_column ...
INDEX index1(immutable_column)
....
) ENGINE=INNODB;
CREATE TABLE restricter (
.....
col1,
INDEX index2(col1),
FOREIGN KEY (col1) REFERENCES original (immutable_colum) ON UPDATE RESTRICT ON DELETE CASCADE
) ENGINE=INNODB;
Taking the idea a step further (for those of us still stuck with a legacy version of MySQL) you can have BOTH a protected & defaulted create_stamp AND an auto-updating update_stamp as follows:
If you have a table such as
CREATE TABLE `csv_status` (
`id` int(11) NOT NULL primary key AUTO_INCREMENT,
`create_stamp` datetime not null,
`update_stamp` timestamp default current_timestamp on update current_timestamp,
`status` enum('happy','sad') not null default 'happy'
);
Then you can define these triggers on it
drop trigger if exists set_create_stamp ;
create definer = CURRENT_USER trigger set_create_stamp BEFORE INSERT on
csv_status for each row
set NEW.create_stamp = now();
drop trigger if exists protect_create_stamp ;
delimiter //
create definer = CURRENT_USER trigger protect_create_stamp BEFORE UPDATE on
csv_status for each row
begin
if NEW.create_stamp != OLD.create_stamp then
set NEW.create_stamp = OLD.create_stamp;
end if;
end;//
delimiter ;
So I am trying to create trigger that will only execute when a specific column within a table is updated. The table in question is track the column in question is track.track_hits. What I want to do is if that value changes then it inserts a value into a table called play_log. The insert should be the track_id from track and a timestamp.
The code I have done so far is below but it doesn't work, how can it be fixed?
CREATE TRIGGER pi_play AFTER UPDATE ON track
FOR EACH ROW
BEGIN
if :new.track_hits != :old.track_hits
then
INSERT INTO play_log (track_id,access_time)
VALUES (NEW.track_id, NOW())
end if;
END;
NEW and OLD do not need : in front. Also, when creating triggers, you may need to change the delimiter. If you don't change the delimiter, your create trigger statement may be terminated early.
Here's how I would do it:
delimiter |
create trigger pi_play after update on track
for each row
begin
if new.track_hits != old.track_hits then
insert into play_log(track_id, access_time) values (new.track_id, now());
end if;
end|
delimiter ;
Here's the fiddle showing it in action: http://sqlfiddle.com/#!2/3d99d/1 Notice that I changed the delimiter in the schema window to | instead of the default ;. Since SQL uses JDBC and JDBC doesn't take the command DELIMITER, I chose the delimiter.
What is the postgres equivalent of the below mysql code
CREATE TABLE t1 (
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE t2 (
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
As per Alex Brasetvik answer below, it seems i should go with triggers, my problem is i have a number of tables t1, t2... with created and modified fields, is it possible to write a generalized procedure?
--update
Almost ready
CREATE FUNCTION update_timestamp() RETURNS trigger AS $update_timestamp$
BEGIN
NEW.modified := current_timestamp;
RETURN NEW;
END;
$update_timestamp$ LANGUAGE plpgsql;
CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON t1
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON t2
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
Just make sure all tables have the same columnname:
CREATE OR REPLACE FUNCTION upd_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
CREATE TRIGGER t_name
BEFORE UPDATE
ON tablename
FOR EACH ROW
EXECUTE PROCEDURE upd_timestamp();
Thank you for the information Mithun and Alex Brasetvik.
I'd like to add one minor tweak to the trigger. Since we mostly likely want the modified column to store the timestamp when the row was last changed, not when it was the target of an UPDATE statement, we have to compare the new and the old value of the row. We update the modified column only if these two values differ.
CREATE OR REPLACE FUNCTION update_modified_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
IF (NEW != OLD) THEN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END IF;
RETURN OLD;
END;
$$;
This trigger ensures that the modified column is updated only if the UPDATE operation actually changes the values stored in the row.
Update it with a trigger. Documentation and examples.
To further improve, the answer given by #Lauri Silvennoinen:
This trigger uses the WHEN clause as recommended by the official docs to check for changes in the row even before calling the specified function.
CREATE OR REPLACE FUNCTION update_modified_timestamp() RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
NEW.modified = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
CREATE OR REPLACE TRIGGER tg_update_modified
AFTER UPDATE ON table_name
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE FUNCTION update_modified_timestamp();