Am I using too many triggers within mySQL database? - mysql

I had a problem within my database where clients would update their information on the website, and the update would overwrite their original information in the database. To have a record of the clients previous information and when they updated it to the 'new' information, I have created a trigger for the specific database.
Example:
CREATE TRIGGER clientstatustrigger AFTER UPDATE ON clientstatus
FOR EACH ROW
BEGIN
INSERT INTO clientstatusrecord SET id = NEW.id, oldstatus = OLD.status, newstatus = NEW.status, timechanged = NOW();
END;
It has worked really great so far! However, I will need to implement this technique for a lot of tables. I will be creating a "record" table for each table I need to track the changes within, and one trigger for those tables. My question is, is this too many triggers? Will I see disadvantages in the future? I understand triggers are hard to debug since people forget they are there, but is there reason for me to change this system? If so, what do you recommend?

Related

MySql Trigger without writing access

So I want to write a trigger that updates (insert, update, delete) a table if another table(in another database) gets updated, something like this for example:
CREATE TRIGGER new_data
AFTER INSERT ON account
FOR EACH ROW
INSERT INTO test4.bank3
SET
money = NEW.amount
The problem is that I only have reading access to the other database (in this example where account lies on).
Is there a way around it or do I have to use a completely different method?
Can you ask the admin of the other database to set up a connection / trigger so that a duplicate table is made in your database? Then you could use that table as the trigger?

How to create changelog for table?

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;
$$

SQL Trigger not on rows but on attributes

Hey guys a little question for you.
I'm currently working on SQL Triggers and my goal is to archive logging if there are changes made to our database. For example we got some tables like customers with: name, firstname, placeofbirth and so on. We offer the users to update their own data and want to save the OLD data in a new table for logging reasons. To have only one logging table for all updates the logging table is kind of generic with:
id, timestamp, table_name, column, old_value, new_value.
table_name is the updated table, colum the updated column in this table and all the rest should speak for itself. Therefore it would be great to know not only in which tuple but also in which particular column the update has happened.
My question: Is there a construct like:
create trigger logging_trigger on customer**.firstname** after insert ...
to trigger an action only if there happened an update on let's say the 'firstname' column?
If not is there a smooth solution for handling all possible update cases?
Thank you.
I use a format like you described in my system... Below is how I accomplish it with your required logic.
CREATE DEFINER = CURRENT_USER TRIGGER `testing_schema`.`new_table_BEFORE_UPDATE` BEFORE UPDATE ON `new_table` FOR EACH ROW
BEGIN
IF NEW.ColumnName <> OLD.ColumnName THEN
INSERT INTO HistoryTable (`ColumnName1`, `ColumnName2`, ect..) VALUES (OLD.ColumnName1, OLD.ColumnName2, ect...);
END IF;
END
The main difference In mine is, that I do not have an IF condition. I simply copy the entire row to the history table every time an Update/Delete is made to that row. That way I don't have to maintain any form of logic to handle scenarios of investigating "what changed", I just save the entire row because I know "something" changed.

MySQL - change tracking of the *data* in the DB?

I have a situation where I need to keep track of all changes to the data in a MySQL database.
For example, we have a field in the "customers" table which will contain a rating that indicates how risky it is to do business with that customer. Whenever this field is changed, I need to have it logged so we can go back and say "well they were a 3 and now they are an 8," for example. Is there any automated way to handle this in MySQL or am I just going have to write tons of change tracking logic into the application itself?
This is the type of thing that triggers are designed for inside of MySQL assuming you're using a 5+ version of MySQL.
CREATE TRIGGER log_change_on_table BEFORE UPDATE ON customers
FOR EACH ROW
BEGIN
INSERT INTO customer_log (customer_id, rating, date)
VALUES (OLD.customer_id, OLD.rating, now())
END $$

Setting Up MySQL Triggers

I've been hearing about triggers, and I have a few questions.
What are triggers?
How do I set them up?
Are there any precautions, aside from typical SQL stuff, that should be taken?
Triggers allow you to perform a function in the database as certain events happen (eg, an insert into a table).
I can't comment on mysql specifically.
Precaution: Triggers can be very alluring, when you first start using them they seem like a magic bullet to all kinds of problems. But, they make "magic" stuff happen, if you don't know the database inside out, it can seem like really strange things happen (such as inserts into other tables, input data changing, etc). Before implementing things as a trigger I'd seriously consider instead enforcing the use of an API around the schema (preferably in the database, but outside if you can't).
Some things I'd still use triggers for
Keeping track of "date_created" and "date_last_edited" fields
Inserting "ID"'s (in oracle, where there is no auto id field)
Keeping change history
Things you wouldn't want to use triggers for
business rules/logic
anything which connects outside of the database (eg a webservice call)
Access control
Anything which isn't transactional ( anything you do in the trigger MUST be able to rollback with the transaction )
From dev.mysql.com, a trigger is
...a named database object that is
associated with a table and that is
activated when a particular event
occurs for the table.
The syntax to create them is also documented at that site.
Briefly,
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_stmt
And they provide an example:
CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
CREATE TRIGGER ins_sum BEFORE INSERT ON account FOR EACH ROW SET #sum = #sum + NEW.amount;
You at least need to abide by all the restrictions on stored functions.
You won't be able to lock tables, alter views, or modify the table that triggered the trigger. Also triggers may cause replication problems.
A trigger is a named database object that is associated with a table and that is activated when a particular event occurs for the table.
To create a trigger:
CREATE TRIGGER triggerName [BEFORE|AFTER] [INSERT|UPDATE|DELETE|REPLACE] ON tableName FOR EACH ROW SET stuffToDoHERE;
Even though I answered this part the other question still stands.
This question is old and other answers are very good, but since the user asked about precautions that should be taken, I want to add something:
If you use replication in a complex environment, don't make a massive use of Triggers, and don't call stored procedures from triggers.
Triggers are slow in MySQL.
You can't use some SQL statements within triggers. And some statements are permitted but should be avoided, like LOCK. The general rule is: if you don't fully understand the implications of what you are doing, you shouldn't do it.
Triggers can cause endless loops, so be careful.