MySQL trigger creating error when migrating from DB2 - mysql

I am trying to convert some DB2 queries to MySQL . I have tried to find some tools to convert the conversion . Unfortunately i din't find any conversions tools and some of the online tools are not converting properly. So i have tried to convert DB2 queries myself to MySQL .
I have started to create 3 trigger queries in MySQL . Unfortunately i am getting some syntax errors . These are the following queries were i migrated from DB2 to MySQL .
Trigger 1 :
CREATE TRIGGER SAMPLE_TABLE3 AFTER INSERT ON SAMPLE_TABLE4
FOR EACH ROW
BEGIN
INSERT INTO LICENSE_REVISION(LICENSE, LICENSE_REV) VALUES (NEW.ID, (NEXT VALUE FOR REVISION));
END;
Here i am getting syntax error
right syntax to use near 'VALUE FOR REVISION));
END' at line 9
I think the problem due to NEXT VALUE FOR .
Question 1 :
Is it any NEXT VALUE FOR alternative for MySQL ?
In trigger 2 , i am doing
CREATE TRIGGER SAMPLE_TRIGGER AFTER UPDATE OF SUPPORT__SERVER_UPS ON SETTINGS
FOR EACH ROW
BEGIN ATOMIC
FOR d AS SELECT ID AS D_ID FROM DOMAIN
DO UPDATE DOMAIN_REVISION DR SET DR.SERVER_DOMAIN_REV = (NEXT VALUE FOR REVISION) WHERE DR.DOMAIN = D_ID;
END FOR;
END;
and getting error :
right syntax to use near 'OF SUPPORT__SERVER_UPS ON SETTINGS
REFERENCING NEW AS NEW OLD AS OLD
FOR EAC' at line 6
i think the problem is AFTER UPDATE OF with column and i am suspecting FOR d AS line 4 and BEGIN ATOMIC too .
Question 2 :
Can i use AFTER UPDATE OF with column in MySQL ? and any mistakes
in FOR d AS line no 4 and BEGIN ATOMIC line 3 .
if i have use multiple column values like
trigger :
CREATE TRIGGER SAMPLE_4 AFTER UPDATE OF IPV4_FIRST, IPV4, IPV4_MASK, IPV6_FIRST, IPV6, IPV6_MASK ON VIRTUAL_NETWORK
FOR EACH ROW
BEGIN
FOR d AS SELECT DOMAIN AS D_ID FROM DOMAIN_VIRTUAL_NETWORK WHERE VIRTUAL_NETWORK=NEW.ID
DO UPDATE DOMAIN_REVISION DR SET DR.CLIENT_NETWORK_REV = (NEXT VALUE FOR REVISION) WHERE DR.DOMAIN = D_ID;
END FOR;
END;
How can i approach this problem ? Should i use like this
IF NEW.a <> OLD.a or NEW.b <> OLD.b ?
I am beginner in this and i want to help to migrate . I know these are syntax problem and not valid questions . Please help or suggestion .
Update :
I have tried with last_insert_id and CURSOR . This is the working code now :
CREATE TRIGGER SAMPLE_TRIGGER AFTER UPDATE ON SETTINGS
FOR EACH ROW
BEGIN
DECLARE my_cursor CURSOR FOR SELECT ID AS D_ID FROM DOMAIN;
IF NEW.SUPPORT__SERVER_UPS <=> OLD.SUPPORT__SERVER_UPS THEN
open my_cursor;
my_loop: loop
FETCH my_cursor INTO D_ID;
UPDATE DOMAIN_REVISION DR SET DR.SERVER_DOMAIN_REV = (last_insert_id()) WHERE DR.DOMAIN = D_ID;
end loop my_loop;
close my_cursor;
END IF;
END;

Sequences
MySQL doesn't have sequence objects. NEXT VALUE FOR <sequence> is not supported in MySQL.
There's an awkward workaround to simulate a sequence by creating a single table that you increment every time you want a new value.
mysql> create table sequence (id int not null primary key );
mysql> insert into sequence values (0);
mysql> update sequence set id = last_insert_id(id+1);
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 1 |
+------------------+
But every time you would use NEXT VALUE FOR <sequence> you would repeat the last two steps above. The last_insert_id() function in MySQL is more or less like DB2's IDENTITY_VAL_LOCAL(). Read full docs on last_insert_id() function here: https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id
Column triggers
MySQL trigger declaration syntax is like
CREATE TRIGGER SAMPLE_TRIGGER AFTER UPDATE ON SETTINGS
FOR EACH ROW ...
MySQL supports no "OF" clause. That's used in DB2 for triggering on the update of specific columns, but MySQL doesn't support triggers on specific columns.
Loops
The FOR d AS SELECT ... loop construct is not supported by MySQL. If you need to do a loop, you need to learn about CURSOR syntax. See examples at https://dev.mysql.com/doc/refman/5.7/en/cursors.html

Related

How to create a Trigger within sql

I have been trying to create a Trigger, however my attempts have been unsuccessful. I seem to be getting an error (#1064), which I have no solution for. Can somebody explain or demonstrate any faults in the syntax.
Let me specify:
I have delivery_id as primary key in delivery table,
I also have delivery_id as a foreign key in entry_log table.
By comparing both id's(if true), will return a text referring to the output of the bit (either 0 or 1)
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
DECLARE #xentry VARCHAR(45)
DECLARE #inta bit
SET #inta = SELECT allowed
FROM delivery
WHERE delivery.delivery_id = entry_log.delivery_id;
CASE
when #inta = 0 then #xentry = 'Acces Denied'
when #inta = 1 then #xentry = 'Acces Allowed'
END CASE
INSERT INTO entry_log(entry_time,access_allowed) VALUES(now(),#xentry);
END
//
This is assuming that you use MySQL. In the body of the trigger you use
WHERE delivery.delivery_id = entry_log.delivery_id;
I think you want to compare to the entry_log entry that the trigger is running on, right? In that case you must use this syntax:
WHERE delivery.delivery_id = NEW.delivery_id;
see here for more examples.
UPDATE
I see that also you try to do an INSERT INTO entry_log within the TRIGGER. This will of course not work, because you would create an infinite recursive loop. Within the
body of the trigger you can do unrelated table access, but not into the table you are inserting. You can change the values to be inserted by the trigger by setting NEW.xyz = whatever
UPDATE 2
I doubt, that your CASE statement is correct. At least it must end with END CASE. You can use IF here, since you don't have many cases to address. If you must use CASE this post might help you: MYSQL Trigger set datetime value using case statement
UPDATE 3
I am not sure, but I think you need brackets around the variable setting statement. try this trigger definition:
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
SET #inta = (SELECT allowed
FROM delivery
WHERE delivery.delivery_id = NEW.delivery_id);
SET NEW.access_allowed = #inta;
SET NEW.entry_time = NOW();
END
//
Note, that this is written out of my head, so beware of syntax errors in my script.

Mysql dropping inserts with triggers

Using mysql 5.6. I have two tables. One has a whitelist of hashes. When I insert a new row into the other table, I want to first compare the hash in the insert statement to the whitelist. If it's in the whitelist, I don't want to do the insert (less data to plow through later). The inserts are generated from another program and are text files with sql statements.
I've been playing with triggers, and almost have it working:
CREATE TRIGGER `Filelist` BEFORE INSERT ON `filelist`
FOR EACH ROW BEGIN IF(
SELECT count( md5hash ) FROM whitelist WHERE md5hash = new.hash ) >0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Can not have duplicates';
END IF ;
END
But there's a problem. The Signal throwing up the error stops the import. I want to skip that line, not stop the whole import.
Some searching didn't find any way to silently skip the import.
My next idea was to create a duplicate table definition, and redirect the insert to that dup table. But the old and new don't seem to apply to table names.
Other then adding an ignore column to my table then doing a mass drop based on that column after the import, is there any way to achieve my goal? I'm having problems with this too [Ignore is a tinyint(1)]:
DELIMITER $$
CREATE TRIGGER whitelisted
BEFORE INSERT ON filelist
FOR EACH ROW BEGIN
IF (select count(md5hash) from whitelist where md5hash=new.hash) > 0 THEN
SET Ignore = true;
END IF;
END$$
/* This is now "END$$" not "END;" */
/* Reset the delimiter back to ";" */
DELIMITER ;
#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 ') THEN SET Ignore = true;
END IF; END' at line 4
Any suggestions? I've also tried
SET Ignore = 1;
SET Ignore = '1';
SET new.Ignore = {all of the above};
I'm not sure if I follow this specification:
I have two tables. One has a whitelist of hashes. When I insert a new row into the other table, I want to first compare the hash in the insert statement to the whitelist. If it's in the whitelist, I don't want to do the insert
My first attempt would be like this:
INSERT INTO filelist (filename, hash)
SELECT "myfile", "ABCD" FROM DUAL
WHERE NOT EXISTS(
SELECT md5hash FROM whitelist where md5hash = "ABCD"
);
I don't think you need triggers for this at all unless there are missing details in your requirements.
I take it you're doing some kind of ON INSERT-trigger.
You need to add the following statement to your trigger to make it work as wanted:
FOR EACH ROW
This will make the trigger execute once on every row.

Trigger - Error #1442 Can't update [MYSQL] [duplicate]

MySQL doesn't currently support updating rows in the same table the trigger is assigned to since the call could become recursive. Does anyone have suggestions on a good workaround/alternative? Right now my plan is to call a stored procedure that performs the logic I really wanted in a trigger, but I'd love to hear how others have gotten around this limitation.
Edit: A little more background as requested. I have a table that stores product attribute assignments. When a new parent product record is inserted, I'd like the trigger to perform a corresponding insert in the same table for each child record. This denormalization is necessary for performance. MySQL doesn't support this and throws:
Can't update table 'mytable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. A long discussion on the issue on the MySQL forums basically lead to: Use a stored proc, which is what I went with for now.
Thanks in advance!
You can actually up the rows in the same table as the trigger. The thread you linked to even has the solution.
For example:
TestTable ( id / lastmodified / random )
create trigger insert_lastmod
before insert on TestTable
for each row
set NEW.lastmodified = NOW();
insert into TestTable ( `random` ) values ( 'Random' );
select * from TestTable;
+----+---------------------+---------------------+
| id | lastmodified | random |
+----+---------------------+---------------------+
| 1 | 2010-12-22 14:15:23 | Random |
+----+---------------------+---------------------+
I suppose you could call the stored proc in your trigger. HOwever, if you want to update some fields in the same records that you are changing (such as an updatedby or lastupdated column) then you can do this in a beofre trigger according to the refernce manual. http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html
This is a common operation for triggers and I find it difficult to believe it isn't supported.
If you want to update column that you don't read in trigger function, then as a workaround, you could put that column into separate table.
You can actually do that
The below is an example for same
DELIMITER $$
create trigger test2
before insert on ptrt
for each row
begin
if NEW.DType = "A" then
set NEW.PA = 500;
elseif NEW.DType = "B" then
set NEW.PA = 1000;
else
set NEW.PA = 0;
END IF;
END;$$
DELIMITER;
This worked for me :D
On Before / Update.
BEGIN
SET NEW.DateTimeUpdated = NOW();
END

MySQL Trigger to update another table

I have the following two tables in a MySql database:
Bookings
BookingID | ClientID | SeatID
SeatAvailability
SeatAvailabilityID | BookingID | ShowID | Available
They are linked on SeatID/SeatAvailabilityID.
I'm trying to write a trigger which updates the SeatAvailability table each time a row is inserted in Bookings. The trigger should change SeatAvailability.Available to 0 and also enter the BookingID from Bookings into the BookingID field in SeatAvailability with the same SeatAvailabilityID.
I've written this trigger, MySql accepts it but gives an error when inserting
"ERROR 1054: Unknown column 'cinemax.bookings.SeatID' in 'where clause'".
DELIMITER $$
USE `cinemax`$$
CREATE
DEFINER=`root`#`localhost`
TRIGGER `cinemax`.`update_available`
AFTER INSERT ON `cinemax`.`bookings`
FOR EACH ROW
UPDATE cinemax.seatavailability
SET cinemax.seatavailability.Availabe=0, cinemax.seatavailability.BookingID=cinemax.bookings.BookingID
WHERE cinemax.bookings.SeatID=cinemax.seatavailability.SeatAvailabilityID$$
try
AFTER INSERT ON `cinemax`.`bookings`
instead of
AFTER UPDATE ON `cinemax`.`bookings`
It's a couple of months late, but I decided to give it a quick shot before handing in the overall assignment. In the meantime I switched to postgres as it seemed to offer more functionality (albeit not as user friendly). I first had to create a trigger function:
CREATE OR REPLACE FUNCTION updateseatavailable()
RETURNS trigger AS
$BODY$
BEGIN
IF (TG_OP = 'INSERT') THEN
UPDATE "SeatAvailability"
SET "Available"='FALSE' AND "BookingID"=NEW."BookingID" WHERE "SeatAvailabilityID"=NEW."SeatID";
ELSIF (TG_OP = 'DELETE') THEN
UPDATE "SeatAvailability"
SET "Available"='TRUE' WHERE "SeatAvailabilityID"=OLD."SeatID";
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
and then simply call the function/procedure from a trigger:
CREATE TRIGGER UpdateSeatAvailable
AFTER INSERT OR DELETE ON "Bookings"
FOR EACH ROW
EXECUTE PROCEDURE updateSeatAvailable();
I wasn't able to get the BookingID in SeatAvailability to update for some reason (on Insert nothing happened and on Delete I got an error telling me Available cannot be null, even though I was changing the BookingID) so I omitted that in postgres,and implemented it with Java instead. It's not the best way but still better than nothing.
I decided to post my solution just in case someone has a similar problem and stumbles upon this question.

MySQL triggers cannot update rows in same table the trigger is assigned to. Suggested workaround?

MySQL doesn't currently support updating rows in the same table the trigger is assigned to since the call could become recursive. Does anyone have suggestions on a good workaround/alternative? Right now my plan is to call a stored procedure that performs the logic I really wanted in a trigger, but I'd love to hear how others have gotten around this limitation.
Edit: A little more background as requested. I have a table that stores product attribute assignments. When a new parent product record is inserted, I'd like the trigger to perform a corresponding insert in the same table for each child record. This denormalization is necessary for performance. MySQL doesn't support this and throws:
Can't update table 'mytable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. A long discussion on the issue on the MySQL forums basically lead to: Use a stored proc, which is what I went with for now.
Thanks in advance!
You can actually up the rows in the same table as the trigger. The thread you linked to even has the solution.
For example:
TestTable ( id / lastmodified / random )
create trigger insert_lastmod
before insert on TestTable
for each row
set NEW.lastmodified = NOW();
insert into TestTable ( `random` ) values ( 'Random' );
select * from TestTable;
+----+---------------------+---------------------+
| id | lastmodified | random |
+----+---------------------+---------------------+
| 1 | 2010-12-22 14:15:23 | Random |
+----+---------------------+---------------------+
I suppose you could call the stored proc in your trigger. HOwever, if you want to update some fields in the same records that you are changing (such as an updatedby or lastupdated column) then you can do this in a beofre trigger according to the refernce manual. http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html
This is a common operation for triggers and I find it difficult to believe it isn't supported.
If you want to update column that you don't read in trigger function, then as a workaround, you could put that column into separate table.
You can actually do that
The below is an example for same
DELIMITER $$
create trigger test2
before insert on ptrt
for each row
begin
if NEW.DType = "A" then
set NEW.PA = 500;
elseif NEW.DType = "B" then
set NEW.PA = 1000;
else
set NEW.PA = 0;
END IF;
END;$$
DELIMITER;
This worked for me :D
On Before / Update.
BEGIN
SET NEW.DateTimeUpdated = NOW();
END