UPDATE 2: If I manually replace the EXECUTE code sample below with the actual SQL code to be executed, it works perfectly fine, which tells me the issue is tired to PREPARE, EXECUTE and DEALLOCATE PREPARE.
UPDATE 1: Actually, it appears every single stored procedure has stopped working (e.g. 0 always returned on insert). What's very strange is when I MANUALLY execute the insert, I get the index back just fine. But when it's executed via Stored Procedure, it's always 0. The only thing that's changed is I rebooted my system. That's it. Note this is my local 127.0.0.1 MySQL installation. Any reason why a manual execute of INSERT would return a LAST_INSERT_ID() but a stored procedure of the exact same insert would fail? I am not using commit/rollback in the SPs and haven't changed the default system setting for commits
This is quite the head scratcher. I have a large and complex Stored Procedure that builds a SQL INSERT from dynamic JSON field data. It was working perfectly until I merged multiple updates into a single SP to support transaction rollback (but I haven't put any of that code in yet).
The code executing the SQL select statement is:
SET #mysql := var_sql_insert;
PREPARE SQLStatement1 FROM #mysql;
EXECUTE SQLStatement1;
DEALLOCATE PREPARE SQLStatement1;
SET var_company_main_id = LAST_INSERT_ID();
Below is the SQL code that's being executed:
INSERT INTO company_main (company_owner_account_ref_id,company_name,company_industry_ref_id,company_size_c,company_type_c,company_date_founded) VALUES (1,'TestCompany',2,1,1,'2009/05/06')
The issue is LAST_INSERT_ID() is returning 0 each and every time I execute the SQL statement in the stored procedure, but if I manually execute it, the LAST_INSERT_ID() returns the index just fine. Note the table I'm doing the INSERT to has a properly defined PK Auto-Increment Index, so that's not the issue (which seems to be the #1 cause of the 0 result).
What's even more nuts is this exact same code worked perfectly fine before - it hasn't changed - the only thing I did was put it in a larger stored procedure and add a Label to the BEGIN (because I execute LEAVE on error).
When I walk the code, it executes perfectly; it's just LAST_INSERT_ID() always returns 0 now even when the record is inserted just fine (along with the incremented ID/index).
Any ideas?
So it seems this issue is related to dbForge Studio 2019 for MySQL. I believe it's creating instanced calling in the IDE that can throw off the LAST_INSERT_ID() in cascaded calls. Once I was able to track down and resolve an error that was returning a bad index (yet had no bearing on a completely un-associated INSERT) everything went back to normal. So it seems this issue was caused by the IDE. Took about 3 hours to figure out because the actual debugger built into dbForge was part of the issue. sigh
I need to create a change history of table rows when a certain field is changed. So what I wanted to do is create a trigger on table update. When the field txta changes, I want the whole row to get copied over to debugwhich is a cloned version of msser_210 with an added column for datetime at the end, without data. I would like to add NOW() on change so I would have a timestamp. This is what I have tried to far:
DELIMITER $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON msser_210
FOR EACH ROW
BEGIN
IF OLD.txta != NEW.txta
THEN
INSERT INTO `debug_history` (`idpm`,`posn`,`prnb`,`doid`,`ofcr`,`pidm`,`hitm`,`sitm`,`item`,`dsca`,`igid`,`kitm`,`leng`,`widt`,`hght`,`thik`,`radi`,`quas`,`wght`,`effc`,`colr`,`bdat`,`edat`,`back`,`cuid`,`intb`,`aggr`,`unqu`,`oqua`,`unsq`,`stoc`,`allo`,`hall`,`tqan`,`bqan`,`pkey`,`pric`,`cvqs`,`unsp`,`disc`,`dart`,`ksid`,`anhg`,`txta`,`txti`,`mndn`, `changedate`) VALUES (OLD.idpm,OLD.posn,OLD.prnb,OLD.doid,OLD.ofcr,OLD.pidm,OLD.hitm,OLD.sitm,OLD.item,OLD.dsca,OLD.igid,OLD.kitm,OLD.leng,OLD.widt,OLD.hght,OLD.thik,OLD.radi,OLD.quas,OLD.wght,OLD.effc,OLD.colr,OLD.bdat,OLD.edat,OLD.back,OLD.cuid,OLD.intb,OLD.aggr,OLD.unqu,OLD.oqua,OLD.unsq,OLD.stoc,OLD.allo,OLD.hall,OLD.tqan,OLD.bqan,OLD.pkey,OLD.pric,OLD.cvqs,OLD.unsp,OLD.disc,OLD.dart,OLD.ksid,OLD.anhg,OLD.txta,OLD.txti, OLD.mndn, NOW());
END IF;
END;
$$
Why I want to do this is because we are having (probably) a php script with a bug that writes the same text string into every field of the database but we don't know when or why it happens neither which script it does. Is there maybe a more elegant solution?
UPDATE: I found the option to "Track Changes" in phpMyAdmin, but apparently it does not track our programs php-issued UPDATE queries, the DROP and CREATE TABLE statements from PHP are tracked though. If I issue an UPDATE via phpMyAdmin, it is tracked though. Long story short I went back to my original plan with the trigger.
UPDATE2: found the answer out myself
Update: As per the OP's comment, clearly the context is very specific. An infrastructure team without access to (or the ability to feedback and direct the development team's) code needs a mechanism by which to log table changes on a production database.
Warnings about using triggers:
Triggers can be tricky to debug, not least because they're transparent and it is never obvious to someone new looking at your code that a trigger is performing some action behind the scenes. (I speak from experience.) They can also cause issues on replicated, multi-master and clustered installations. (Again, I speak from experience.) Also if they fail for some unrelated reason (e.g. the table they write to is broken), the entire transaction can/will fail (InnoDB) - which might not be what you want. (Especially with non-essential "debug" functions.)
Otherwise, triggers are a perfectly valid tool. And in your specific scenario, probably the best bet available to you.
There are several other options available to you, two of which I would highlight:
Stored procedures as an access layer to data
If you're very data centric and you already have business logic inside the database - (a hotly debated topic, I'm not here arguing that you should or should not have business logic in the database) then reading and writing to the database through stored procedures has a clear advantage.
Any transactionally tied logic can be inserted into these stored procedures such that the transactionally unsafe caller (PHP, being a common example) only needs to call 1 query (call sp_insert_tablename(123, 'abc')) and transactional safety can be enforced by the database.
Temporary debug logic can be added to these stored procedures and enabled/disabled by a flag in a settings table, session variable, final argument, whatever you please.
Data abstraction layer/library
Similar principle. Find a data abstraction layer for your client (assuming you have access to alter it's internals). For a PHP or .NET web app there are several popular choices, all of which allow you to override (extend through code inheritance) the save/delete operations to perform any additional actions you want - exactly as for stored procedures (but with the logic maintained inside models in the client).
If you want a specific example, you'll need to give us more information on what stack/language/framework(s) you're using
With both options, make sure you appropriately handle error scenarios.
The debug_history is a cloned via pypMyAdmin from the original table. It got an additional changedate column appended manually.
ALTER TABLE debug_history ADD COLUMN changedate DATETIME DEFAULT NULL;
I decided because there was no other way that I would have to type all the names myself. Because I am lazy I got a recent SQL dump, copied an INSERT INTO-Statement from the file that is used to rebuild msser_210 and altered the values.
I added an extra row with an autoincrement line, dropped the primary key and set the new primary key to the new row.
ALTER TABLE debug_history DROP PRIMARY KEY;
ALTER TABLE debug_history ADD COLUMN changenumber INT NOT NULL PRIMARY KEY AUTO_INCREMENT;
I now have a working changelog, triggered on change in txta field (Please see the question for the trigger with the original format). I renamed the txta column in the debug_history to txta_old and created a new column txta_new.
ALTER TABLE debug_history CHANGE txta txta_old TEXT NOT NULL $$
ALTER TABLE debug_history ADD COLUMN txta_new TEXT NOT NULL AFTER txta_old $$
Afterwards I had to modify the trigger because I manually had to copy all the names..
DROP TRIGGER history_trigger
DELIMITER $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON msser_210
FOR EACH ROW
BEGIN
IF OLD.txta != NEW.txta
THEN
INSERT INTO `debug_history` (`idpm`,`posn`,`prnb`,`doid`,`ofcr`,`pidm`,`hitm`,`sitm`,`item`,`dsca`,`igid`,`kitm`,`leng`,`widt`,`hght`,`thik`,`radi`,`quas`,`wght`,`effc`,`colr`,`bdat`,`edat`,`back`,`cuid`,`intb`,`aggr`,`unqu`,`oqua`,`unsq`,`stoc`,`allo`,`hall`,`tqan`,`bqan`,`pkey`,`pric`,`cvqs`,`unsp`,`disc`,`dart`,`ksid`,`anhg`,`txta_old`,`txta_new`,`txti`,`mndn`, `changedate`) VALUES (OLD.idpm,OLD.posn,OLD.prnb,OLD.doid,OLD.ofcr,OLD.pidm,OLD.hitm,OLD.sitm,OLD.item,OLD.dsca,OLD.igid,OLD.kitm,OLD.leng,OLD.widt,OLD.hght,OLD.thik,OLD.radi,OLD.quas,OLD.wght,OLD.effc,OLD.colr,OLD.bdat,OLD.edat,OLD.back,OLD.cuid,OLD.intb,OLD.aggr,OLD.unqu,OLD.oqua,OLD.unsq,OLD.stoc,OLD.allo,OLD.hall,OLD.tqan,OLD.bqan,OLD.pkey,OLD.pric,OLD.cvqs,OLD.unsp,OLD.disc,OLD.dart,OLD.ksid,OLD.anhg,OLD.txta,NEW.txta,OLD.txti, OLD.mndn, NOW());
END IF;
END;
$$
Hypothetically, I am going to develop a trigger that inserts a record to Table A when an insertion made to an Table A.
Therefore, I want to know how the system handles that kind of loophole or it is going to continue as a loop until the system hangs which requires restart and possibly remove the DB.
I'm trying to gather information on almost every DBMS on this issue or loophole.
I can only speak to Oracle, I know nothing of MySQL.
In Oracle, this situation is known as mutation. Oracle will not spiral into an endless loop. It will detect the condition, and raise an ORA-04091 error.
That is:
ORA-04091: table XXXX is mutating, trigger/function may not see it
The standard solution is to define a package with three functions and a package level array. The three functions are as follows:
initialize - this will only zero out the array.
save_row - this will save the id of the current row (uk or pk) into the arrray.
process_rows - this will go through the array, and actually do the trigger action for each row.
Now, define some trigger actions:
statement level BEFORE: call initialize
row level BEFORE or AFTER: call save_row
statement level AFTER: call process_rows
In this way, Oracle can avoid mutation, and your trigger will work.
More details and some sample code can be found here:
https://asktom.oracle.com/pls/asktom/ASKTOM.download_file?p_file=6551198119097816936
You can only insert a record in same table if you are using instead of trigger. In all other cases you can only modify the record being inserted.
I hope this answers your quest.
you can create trigger in mysql DBMS.
check below link for create insert trigger syntex
http://www.techonthenet.com/oracle/triggers/after_insert.php
I created a simple UPDATE AFTER INSERT trigger to sum two DECIMAL(10,2) fields and update a 3rd DECIMAL(10,2) field with the summed value.
The trigger code is:
delimiter |
CREATE TRIGGER calc_ttl_cost AFTER INSERT ON buybox_rec
FOR EACH ROW
BEGIN
UPDATE buybox_rec SET total_cost = (price+shipping_cost);
END;
|
I'm using phpMyAdmin to run the SQL commands, so I know that the trigger is being created successfully. Furthermore, if I run the UPDATE buybox_rec SET total_cost = (price+shipping_cost); SQL statement alone, it works as expected. (I've also tried the same code with back-ticks around the field names, but I wouldn't be writing all this if that worked)
The problem is that when I insert a new row, the trigger doesn't work and this error is thrown:
#1442 - Can't update table 'buybox_rec' in stored function/trigger
because it is already used by statement which invoked this stored
unction/trigger.
The error seems like some sort of recursive or circular reference problem, but I can't figure out where/what the problem is.
I also tried creating a stored procedure and calling it from within the trigger to try obtaining the same result, but I got the same error.
I checked a bunch of other SO questions related to MySQL UPDATE triggers and did some Googling, but here I am.
Is this out of the scope of MySQL? It seems like such a common and easy task to allow for.
Does anyone know how I accomplish this task autonomously? (AKA I don't want to hear about summing the fields after the fact via PHP, for example)
THanks for any help
The problem is that you're trying to modify the contents of a table which is already being used by the UPDATE + TRIGGER operation. This simply can't be done, but you have alternatives.
For example, if your meaningful data (or independent variables) are price and shipping cost while the total cost depends on them, you could keep only the first two in your table, and maybe have a very simple VIEW (something like SELECT price, shipping_cost, price+shipping_cost total_cost FROM buybox_rec, or whichever other fields you need) if you want to keep an eye at the total.