SQL Trigger on Update, Insert, Delete on non-specific row, column, or table

我怕爱的太早我们不能终老 提交于 2020-01-24 21:50:53

问题


I have several databases that are used by several applications (one of which is our own, the others we have no control over in what they do).

Out software has to know when the database has last been changed. For reasons I won't get into to keep this short we decided that going with a new table per database that has a singular field: last_changed_on that has a GetDate() as a value. This way our own software can check when it was last changed and check it to the date it has stored for said database and do things if the date is newer than what is stored in-memory.

After doing some research we decided that working with Triggers was the way to go, but from what I could find online, triggers look at specific columns that you set for Updates.

What I'd like to know is if there is a way to automate the process or just have a trigger that happens whenever anything happens insert, update, remove wise?

So I am looking for something like this:

CREATE TRIGGER LastModifiedTrigger
ON [dbo].[anytable]
AFTER INSERT, UPDATE, DELETE
AS

   INSERT INTO dbo.LastModifiedTable (last_modified_on) VALUES (CURRENT_TIMESTAMP)

I know that the above example isn't a correct trigger, I'm rather new to them so I was unsure on how to word it. It might be interesting to note that I can have my own software run several queries creating the queries automatically for each table and each column, but I'd rather avoid to do that as keeping track of all those triggers will be a pain in the long run. I'd prefer to have a little triggers per database as possible, if only by not having to make a trigger for each individual column name.

Edit: To clarify: I am trying to avoid having to create an automated script that goes and scans every table, and sequentially every column of every table, to create a trigger to see if something is changed there. My biggest issue at the moment is the trigger behavior on updates, but I'm hoping to avoid having to specify tables as well for insert and delete

Edit 2: To avoid future confusion, I'm looking for a solution to this problem for both SQL Server (MS SQL/T SQL) and MySQL

Edit 3: Turns out that I read the documentation very wrongly and (at least on MySql) the trigger activates on any given updated column without having to define a specific one. Regardless, I'm still wondering if there is a way to just have less triggers than having one for each table in a database. (i.e. 1 for any type of update(), 1 for any type of insert(), and 1 for any type of delete()

EDIT 4: Forgot that the argument for overwriting 1 field will come with performance issues, I've considered this and I'm now working with multiple rows. I've also handled the creating of 3 triggers (insert(), update(), and delete()) for each database through my software's code, I really wished this could've been avoided, but it cannot.

Solution

After a bunch more digging on the internet and keep finding opposite results of what I was looking for, and a bunch of trial and error, I found a solution. First and foremost: having triggers not being dependent on a table (aka, the trigger activates for every table is impossible, it cannot be done, which is too bad, it would've been nice to keep this out of the program code, but nothing I can do about it.

Second: the issue for updates on not being column specific was an error due to my part for searching for triggers not being dependent on specific columns only giving me examples for triggers that are. The following solution works for MySql, I have yet to test this on SQL Server, but I expect it to not be too different.

CREATE TRIGGER [tablename]_last_modified_insert
   AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
     FOR EACH ROW
     BEGIN
        INSERT INTO [db].last_modified(last_modified_on)
        VALUES(current_timestamp())
     END

As for dynamically creating these triggers, the following show how I get it to work: First Query:

SHOW TABLES

I run the above query to get all the tables in the database, exclude the last_modified I made myself, and loop through all of them, creating 3 triggers for each.

A big thank you to Arvo and T2PS for their replies, their comments helped by pointing me in the right direction and writing up the solution.


回答1:


You're slightly off in the assumption that SQL Server triggers are per-column; the CREATE TRIGGER syntax binds the trigger to the named table for the specified operations. The trigger will be called with two logical tables in scope (inserted & deleted) that contain the rows modified by the operation that caused the trigger to fire; if you wanted to check for specific columns' values or changes, then the trigger logic would need to operate against those logical tables.

If you take this approach, you will need to create a trigger for each table you wish to monitor in this fashion; we've had a similar need to track changes (at a more granular level), we didn't find a "pseudotable" that corresponds to all tables in a schema/database. You should also be aware that locking semantics will come into play by doing this, as you will have triggers from multiple tables all targeting the same row for an update as part of separate operations -- depending on the concurrency model in effect, you could be looking at performance consequences by doing so if you expect multiple DML queries to operate concurrently against your database.

I would suggest checking Arvo's commented link above for suitability instead; querying system views is more likely to avoid contention (and other performance-related) issues from using triggers in your scenario.




回答2:


After a bunch more digging on the internet and keep finding opposite results of what I was looking for, and a bunch of trial and error, I found a solution. First and foremost: having triggers not being dependent on a table (aka, the trigger activates for every table is impossible, it cannot be done, which is too bad, it would've been nice to keep this out of the program code, but nothing I can do about it.

Second: the issue for updates on not being column specific was an error due to my part for searching for triggers not being dependent on specific columns only giving me examples for triggers that are. The following solution works for MySQL, I have yet to test this on SQL Server, but I expect it to not be too different.

CREATE TRIGGER [tablename]_last_modified_insert
   AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
     FOR EACH ROW
     BEGIN
        INSERT INTO [db].last_modified(last_modified_on)
        VALUES(current_timestamp())
     END

As for dynamically creating these triggers, the following show how I get it to work: First Query:

SHOW TABLES

I run the above query to get all the tables in the database, exclude the last_modified I made myself, and loop through all of them, creating 3 triggers for each.




回答3:


Perhaps you could use Audit for SQL Server:

CREATE SERVER AUDIT [ServerAuditName]
TO FILE
(
   FILEPATH = N'C:\Program Files......'
)
ALTER SERVER AUDIT [ServerAuditName] WITH (STATE=ON)
GO   
CREATE DATABASE AUDIT SPECIFICATION [mySpec]
FOR SERVER AUDIT [ServerAuditName]
ADD (INSERT, UPDATE, DELETE ON DATABASE::databasename BY [public])
WITH (STATE=ON)
GO

Then you can query for changes:

SELECT * 
FROM sys.fn_get_audit_file ('C:\Program Files......',default,default);
GO  


来源:https://stackoverflow.com/questions/57954116/sql-trigger-on-update-insert-delete-on-non-specific-row-column-or-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!