MySQL table .new supposedly does not exist, when referencing in trigger - mysql

I have the following three (InnoDB-) tables in a MySQL database:
Entity (DLID)
Category (CatID, CatName)
hasCategory (DLID, CatID)
Now, upon insertion into the table hasCategory I would like to make sure, that each Entity is associated with at least one Category. Thus, I wrote the following trigger:
delimiter |
create trigger Max before insert on hasCategory for each row begin
if (exists (select distinct DLID from Entity where not exists (select distinct new.DLID from new))) then
signal sqlstate '45000'
set message_text = 'Min of 1 category per entity required';
end if;
end|
delimiter ;
Now, when I execute the following query: insert into hasCategory values (1, 1); I get the error error code 1146: table mydb.new does not exist. I have created some other triggers similar to this one, also referring to the new-table, where it worked perfectly well. Yet, I don't get it, what causes the error in this particular trigger.
Is it possible that the select statement causes some trouble? I've read that only select into statements are valid in procedures, but I don't know if this has got anything to do with this.
Thanks for your help!

select distinct new.DLID from new
there is no table new in your DB just as the error states. You can use NEW for the record that will be inserted in your trigger but you cannot use it as a table name and select from it.
try
if (select 1 from Entity where DLID = new.DLID) = 1 then

Related

Unable to create a MySQL/MariaDB Stored Procedure

I have a XML file which contains 200k records. I have to insert or update the records in the database. My database structure is as followed:
+----+------+-------+-----+-----+------------+
| Id | name | stock | sku | ean | updated_by |
+----+------+-------+-----+-----+------------+
For inserting the records i'm using Doctrine. But it takes to long before all records are inserted or updated. Therefore I want to create a stored procedure.
I tried the following stored procedure but it's not working.
delimiter $$
create trigger insert
before insert on Product
for each row
begin
IF(exists(select 1 From Product WHERE sku = new.sku))
THEN
BEGIN
UPDATE Product SET stock = new.stock WHERE sku = sku;
END;
END IF;
end$$
delimiter
Error:
General error: 1442 Can't update table 'Product' in stored
function/trigger because it is already used by statement which invoked
this stored function/trigger
Update:
You can't use a MySQL trigger in the way you describe. You can't update the table for which the trigger is defined. It would result in an infinite loop.
Even if the trigger code you wrote worked, MySQL does not support "instead of" triggers. That is, it would do the UPDATE but then it would also attempt the INSERT. So it would either create another row with the same sku value, or else if you have a unique constraint on sku, the insert would cause an error and roll back both the INSERT and the UPDATE run from the trigger.
This is why the REPLACE and INSERT...ON DUPLICATE KEY UPDATE statements exist.
I understand you want to use ORM methods for all operations, but this will be a good example of a general rule: ORM's do not support all operations. Inevitably you will encounter a case where you have to write SQL if you want to do something special. Doctrine, like all ORM libraries, supports a way to run literal SQL statements, bypassing the ORM methods.
See Doctrine: ON DUPLICATE KEY UPDATE for example.
You should use insert on duplicate
but your trigger should be li9ke this
your first insert must be the trigger name and the end is missing a ;
delimiter $$
create trigger insert_prodzct
before insert on Product
for each row
begin
IF(exists(select 1 From Product WHERE sku = new.sku))
THEN
BEGIN
UPDATE Product SET stock = new.stock WHERE sku = sku;
END;
END IF;
end$$
delimiter ;

MySQL BEFORE INSERT trigger to turn duplicate primary keys inserts into updates

I'm trying to execute this query in a database through phpmyadmin
create trigger avoid_duplicated_sharing
before insert on sharingevents
for each row
begin
if ( select count(*) from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to > 0 ) then
delete from sharingevents where shared_note_id = NEW.shared_note AND shared_to = NEW.shared_to
END IF;
END
But phpmyadmin gives me the following error:
MySQL said: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'END IF' at line 7
Two questions:
What's wrong with my script?
After a BEFORE INSERT trigger, Will INSERT operation be performed? In case it doesn't I will have to remove INSERT INTO SharingEvents (SELECT * FROM NEW);
I solve it with the following code:
delimiter $$
create trigger avoid_duplicated_sharing
before insert on sharingevents
for each row
begin
if ( select count(*) from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to > 0 ) then
delete from sharingevents where shared_note_id = NEW.shared_note_id AND shared_to = NEW.shared_to;
end if;
END$$
The problem was the delimiter.
Even so, my trigger doesn't work. When the application inserts duplicated primary keys MySQL throws the following error:
#1442 - Can't update table 'sharingevents' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
Use exists:
if (exists (select 1 from sharingevents where shared_note_id = new.shared_note_id AND shared_to = new.shared_to) > 0) then
insert into sharingevents (shared_note_id,shared_to,permission_level)
values (NEW.shared_note_id,NEW.shared_to,NEW.permission_level);
end if;
Or, better yet, add a unique index on sharingevents(shared_note-id, shared_to) and then use:
insert into sharingevents (shared_note_id, shared_to, permission_level)
values (NEW.shared_note_id, NEW.shared_to, NEW.permission_level)
on duplicate key update shared_note_id = values(shared_note_id);
This will ignore any updates where the pairs already exist in the table. No if required.
count(shared_note_id, shared_to) is invalid syntax. You can only put multiple column names inside COUNT() when you use count(DISTINCT ...). In your case, you don't need to put column names at all, just use COUNT(*) to count the number of rows matching the condition.
See count(*) and count(column_name), what's the diff? for more information about when you should put column names in COUNT()
Unfortunately, fixing the syntax errors won't really solve your problem, because you can't use a trigger to make a change to the same table. From the FAQ:
Can triggers access tables?
A trigger can access both old and new data in its own table. A trigger can also affect other tables, but it is not permitted to modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger.
You'll need to recode the callers to use INSERT ... ON DUPLICATE KEY UPDATE, or something equivalent, to accomplish this.

MySql - can AFTER INSERT trigger delete the row which invoked it

Let me put it in simplest words possible - is it possible to delete the row, which actually set On the trigger i.e. I have an AFTER INSERT ON <table2> trigger, the SQL in the trigger INSERT / UPDATE another <table1> (based on a WHERE), and finally tends to delete the entry/row in the (the row which basically fired the trigger).
Trigger SQL:
DELIMITER ||
DROP TRIGGER IF EXISTS trg_adddata_tmp ||
CREATE TRIGGER trg_adddata_tmp
AFTER INSERT ON adddata_tmp
FOR EACH ROW
BEGIN
IF EXISTS (SELECT * FROM adddata WHERE data_id = new.data_id AND account_name = new.account_name) THEN
UPDATE adddata SET data_id = new.data_id, account_name = new.account_name, user_name = new.user_name, first_name = new.first_name, last_name = new.last_name WHERE data_id = new.data_id AND account_name = new.account_name;
ELSE
INSERT INTO adddata (data_id, account_name, user_name, first_name, last_name)
VALUES(new.data_id, new.account_name, new.user_name, new.first_name, new.last_name);
END IF;
DELETE FROM adddata_tmp WHERE id = new.id;
END;
||
Without the DELETE (just above the END;) the trigger works fine - UPDATE if exist otherwise INSERT - with DELETE statement gives the following error:
Error Code: 1442
Can't update table 'adddata_tmp' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
By the way, the error is pretty self-explanatory, but still wanted to make sure if this is possible - if not this way, may be some other way around i.e. I want the adddata_tmp table to be empty (or clean-ed up) all the time (on INSERT copies the data to main adddata table)
One idea, I have in mind is, to use an EVENT to clean-up the adddata_tmp based on some status field - which gets set as the last statement in the trigger (in place of DELETE).
No, you can't do this with trigger, here's what the documentation says:
A stored function or trigger cannot modify a table that is already
being used (for reading or writing) by the statement that invoked the
function or trigger.
If adddata_tmp table needs to be empty all the time then I would not write trigger at all. Instead, I would recommend moving adddata update logic in the script/service that tries to insert the data into adddata_tmp.
update
If we are doing bulk inserts and the data (in adddata_tmp table) is not utilised anywhere else then we can write a cron job to clean up the table (i.e. the one that executes let's say every 10 minutes). Also, TRUNCATE would be more efficient (than DELETE) in this case.

mysql trigger for loop

I'm trying to do some mysql trigger coding. I fail every time I meet loops.
CREATE TRIGGER `after_insert` AFTER INSERT ON `table_users`
FOR EACH ROW BEGIN
INSERT INTO table_user_plan (user_id, plan_id) VALUES
(NEW.id, (SELECT id FROM table_plans))
;
END
Here trigger is successfully created, but I get error
#1242 - Subquery returns more than 1 row
I understand this cannot work, because there are more than one row in table_plans... but how can I handle this if I want to add multiple rows or how can I make a loop and firstly select plans then insert into table_users?
Thanks in advanced
To insert multiple rows based on a SELECT you would use the INSERT ... SELECT FROM ... syntax. In this case, you would use something like
INSERT INTO table_user_plan
SELECT NEW.id AS user_id, tp.id AS plan_id
FROM table_plans tp;
(I think that should work, although I've never actually tried to use NEW in this context.)

want to write trigger for two different databases in Mysql

Is there any way to create triggers on two different databases in Mysql? my requirement is like:-
database: test1 -> table: tmp1
database: test2 -> table: tmp2
now I have to use trigger on test1 insert operation happens on tmp1 a value has to be inserted into tmp2 of test2 database. And also vice a verse.
i.e. one more trigger on tmp2 table of test2 database, if insert into tmp2 then inserted into tmp1 table of test1 database.
I have tried to write the trigger on both but I think it will goes into loop to insert each other tables.
DELIMITER $$
CREATE TRIGGER trigger_ad_t1 AFTER insert ON `test1`.tmp1
FOR EACH ROW
Begin
INSERT INTO `test2`.tmp2 VALUES (NEW.employeeNumber,New.fname,New.lname)
END$$
DELIMITER ;
same type of trigger written for insert into tmp1 after insert into tmp2 table.
One more thing I have tested this trigger on my local pc which has mysql 5.1.63 but when I am trying this trigger on my testing server which has mysql 5.0.45 then it gives me syntax error(1064). Don't know what is the problem?
UPDATE:
Can anybody help me to get rid of it.
Thanks
Use fully qualified table names in your trigger.
I.e.
db1.test1.* and d2.test2.*
P.S. After looking at your SQL one more time I realised that you ARE doing the above already.
Edit: Comment field is to restrictive to post code, so here is how you prevent the endless insert loop (assuming employeeNumber is unique key):
Edited code:
IF NOT EXISTS(SELECT employeeNumber FROM otherDB.otherTable WHERE employeeNumber = NEW.employeeNumber) THEN
INSERT INTO otherDB.otherTable VALUES (NEW.employeeNumber,New.fname,New.lname)
END IF;
Correction was needed in the code provided originally:
... EXISTS(SELECT * FROM otherDB.otherTable ...) is replaced with
... EXISTS(SELECT employeeNumber FROM otherDB.otherTable ...)
The reason being that the first query will always return true because the inner query SELECT * FROM ... always returns one record containing the number of results =>
EXISTS(SELECT * FROM ...) is always true