T-SQL Trigger After Specific Insertion - sql

I need to write a trigger that only runs when an insertion that has a certain field with a specific value is inserted. There is no updating.
For example, if I insert an entry that has column X with the value "MAC ID", I want it to run a trigger, but ONLY if the value is "MAC ID".
I know how to test for this normally, but not sure how to test this in a trigger. So far I've found that I want to use "FOR INSERT", but beyond that I don't know how to implement it.
Any help is much appreciated! Thanks!

create trigger MaxIdInsert
on YourTable
after insert
as
if exists
(
select *
from inserted
where ColumnX = 'MAX ID'
)
begin
-- do what you want here if ColumnX has value of 'MAX ID'
end
go
There's no way to only fire a trigger on certain DML specifications (besides insert, update, and/or delete). Your best bet is to test out the dynamic inserted table, which contains records that are inserted into YourTable. In this, you can test for inserted records that have a ColumnX value of "MAX ID".
Edit: In case you were wondering, I know you specified a for trigger in your question. A for trigger is equivalent to an after trigger in SQL Server.

You need to be aware that triggers run once for a batch, not once per row. As such, you may be running in a circumstance in which some of the rows match your criteria, and others do not.
As such, you'd do best to write your trigger logic to select the matching rows from inserted. If there were no matching rows, the rest of your logic will be working with an empty set, but that's no real problem - SQL Server is plenty fast enough at doing nothing, when required.
E.g. if you're inserting these rows into an audit table, something like:
create trigger MacId
on T
for insert
as
insert into Audit(Col1,Col2,Col3)
select i.Col1,i.Col2,'inserted'
from inserted i
where i.Col4 = 'MAC ID'

Related

SQL Server statement explain

Please explain what the following statements mean. It's an assignment of local variables but I do not understand what inserted or deleted means?
select #ID = ID from inserted
select #co_ID = co_ID from deleted
Thank you
INSERTED and DELETED are temporary, memory resident tables created by SQL Server for use (or misuse) within a DML trigger.
Inserts and updates copy new rows into INSERTED,
Deletes and updates copy old rows into DELETED.
It looks like this code is attempting to audit a change to a row of data - but will fail unless there is something else in the code path guaranteeing that only a single row will be updated.
These statements mean that you have written a trigger in SQL Server that is not safe. The trigger assumes that only one row has been updated. This is not safe because SQL Server calls triggers based on groups of rows.
If there is one row, then the parameters #ID and #co_ID are assigned values from that row. If there are multiple rows being updated, then values from arbitrary -- and perhaps different -- rows are assigned to the parameters.

Sql trigger for update in specific column after insert

I made a trigger that is supposed to update another value in the same table a after I make an insert to the table. I do get the result I am looking for, but when I ask my teacher if it is correct he responded that this trigger updates "all" tables(?) and thus incorrect. He would not explain more than that (he is that kind of teacher...). Can anyone understand what he means? Not looking for the right code, just an explanation of what I might have misunderstood.
CREATE TRIGGER setDate
ON Loans
AFTER INSERT
AS
BEGIN
UPDATE Loans
set date = GETDATE()
END;
Your teacher intends to say that the query updates all rows in the table -- perhaps you misunderstood her or him.
The best way to do what you want is to use a default value:
alter table loans alter column date datetime default getdate();
That is, a trigger is not needed. If you did use a trigger, I'll give you two hints:
An instead of trigger.
inserted should be somewhere in the trigger.
Hello there. I upvoted your question because that might be the question of many beginners in SQL Server.
As I see you defined the trigger right! It is a correct way although it's not the best.
By the way we are not discussing all ways you could choose or not, I'm gonna correct your code and explain what your teacher meant.
Look at this UPDATE you wrote:
UPDATE Loans
SET date = GETDATE()
If you write a SELECT without a WHERE clause, what does it do? It would result all the rows in the table which was selected. right?
SELECT * FROM dbo.loans
So without a WHERE clause, your UPDATE will update all of the rows in the table.
OK now what to do to just update the only row ( or rows) which were recently inserted?
When you are writing a trigger, you are allowed to use this table: Inserted
which it has the rows were recently inserted. kinda these rows first come to this table and then go to the final destination table. So you can update them before they go the dbo.loans
it would be like this:
Update dbo.loans
SET date = GETDATE()
WHERE Id IN (SELECT * FROM Inserted)

PL SQL trigger to insert history record when a column is updated

I would like to insert a row into a history table when any column is updated in a table.
I'm just looking to capture the column name, old value and new value.
I'd like this trigger to be as reusable as possible as I'm going to use the same concept on other tables.
I'm familiar with triggers and with how to capture updates on one column. I'm specifically looking for how to write one trigger that inserts a record into a history table for any column that gets updated in the history table's corresponding table.
EDIT 1
I have stated NOWHERE in my post that I'm looking for source code so shame on anyone that downvotes me and thinks that I'm looking for that. You can check my previous questions/answers to see I'm not one looking for "free source code".
As I stated in my original question, I'm looking for how to write this. I've examined http://plsql-tutorial.com/plsql-triggers.htm and there's a code block which shows how to write a trigger for when ONE column is updated. I figured that maybe someone would have the know-how to give direction on having a more generic trigger for the scenario I've presented.
Assuming a regular table rather than an object table, you don't have a whole lot of options. Your trigger would have to be something of the form
CREATE OR REPLACE TRIGGER trigger_name
AFTER UPDATE ON table_name
FOR EACH ROW
BEGIN
IF( UPDATING( 'COLUMN1' ) )
THEN
INSERT INTO log_table( column_name, column_value )
VALUES( 'COLUMN1', :new.column1 );
END IF;
IF( UPDATING( 'COLUMN2' ) )
THEN
INSERT INTO log_table( column_name, column_value )
VALUES( 'COLUMN2', :new.column2 );
END IF;
<<repeat for all columns>>
END;
You could fetch the COLUMN1, COLUMN2, ... COLUMN<<n>> strings from the data dictionary (USER_TAB_COLS) rather than hard-coding them but you'd still have to hard-code the references to the columns in the :new pseudo-record.
You could potentially write a piece of code that generated the trigger above by querying the data dictionary (USER_TAB_COLS or ALL_TAB_COLS most likely), building a string with the DDL statement, and then doing an EXECUTE IMMEDIATE to execute the DDL statement. You'd then have to call this script any time a new column is added to any table to re-create the trigger for that column. It's tedious but not particularly technically challenging to write and debug this sort of DDL generation code. But it rarely is worthwhile because someone inevitably adds a new column and forgets to re-run the script or someone needs to modify a trigger to do some additional work and it's easier to just manually update the trigger than to modify and test the script that generates the triggers.
More generally, though, I would question the wisdom of storing data this way. Storing one row in the history table for every column of every row that is modified makes using the history data very challenging. If someone wants to know what state a particular row was in at a particular point in time, you would have to join the history table to itself N times where N is the number of columns in the table at that point in time. That's going to be terribly inefficient which very quickly is going to make people avoid trying to use the history data because they can't do useful stuff with it in a reasonable period of time without tearing their hair out. It's generally much more effective to have a history table with the same set of columns that the live table has (with a few more added for tracking dates and the like) and to insert one row in the history table each time the row is updated. That will consume more space but it is generally much easier to use.
And Oracle has a number of ways to audit data changes-- you can AUDIT DML, you can use fine-grained auditing (FGA), you can use Workspace Manager, or you can use Oracle Total Recall. If you are looking for more flexibility than writing your own trigger code, I'd strongly suggest that you investigate these other technologies which are inherently much more automatic rather than trying to develop your own architecture.
You might setup the history table to be the SAME as the main table, + a date and type field. You only need to capture the old values, as the new values are in the main table.
try this (untested):
create or replace trigger "MY_TRIGGER"
before update or delete
on MY_TABLE referencing new as new old as old
for each row
declare
l_dml_type varchar2(10);
begin
if (updating) then
l_dml_type := 'UPD';
else
l_dml_type := 'DEL';
end if;
insert into MY_TABLE_HIST
(
col1,
col2,
col3,
dml_type,
dml_date
)
values
(
:old.col1,
:old.col2,
:old.col3,
l_dml_type,
sysdate
);
end;
/
As a note, depending on your design, if space is a limit, you can create a view that would track the changes in the way you were going for, and just show what the record was at the time.

How to get last inserted records(row) in all tables in Firebird database?

I have a problem. I need to get last inserted rows in all tables in Firebird db. And one more, these rows must contain specfied column name. I read some articles about rdb$ but have a few experience with that one.
There is no reliable way to get "last row inserted" unless the table has a timestamp field which stores that information (insertion timestamp).
If the table uses integer PK generated by sequense (generator in Firebird lingo) then you could query for the higest PK value but this isn't reliable either.
There is no concept of 'last row inserted'. Visibility and availability to other transactions depends on the time of commit, transaction isolation specified etc. Even use of a generator or timestamp as suggested by ain does not really help because of this visibility issue.
Maybe you are better of specifying the actual problem you are trying to solve.
SELECT GEN_ID(ID_HEDER,0)+1 FROM ANY_TABLE INTO :ID;
INSERT INTO INVOICE_HEADER (No,Date_of,Etc) VALUES ('122','2013-10-20','Any text')
/* ID record of INVOICE_HEADER table gets the ID_number from the generator above. So
now we have to check if the ID =GEN_ID(ID_HEADER,0) */
IF (ID=GEN_ID(ID_HEADER,0)) THEN
BEGIN
INSERT INTO INVOICE_FOOTER (RELACION_ID, TEXT, Etc) Values (ID, 'Text', Etc);
END
ELSE
REVERT TRANSACTION
That is all

Query to View new/updated tables

I am using the following to list the row count for all of my tables:
select convert(varchar(30),object_name(id)) [Table Name], rows, ModifiedOn from sysindexes
where object_name(id) not like 'sys%' and indid = 1
order by object_name(id)
I confess that I found this somewhere and only have a conceptual idea of what it is doing. But for my purposes, where I want to perform an application action and reverse engineer what happened in the database, it works well to identify new rows (I copy and paste before/after results into excel to compare).
Now, I would also like to know which tables have been updated. On (almost) all of my tables there is a ModifiedOn column, so I am hoping I can add the max of this to my output, which will tell me when the table's contents were last updated.
I have no idea how to join these two, and any help is appreciated.
I would strongly suggest against this approach, as it is DB dependent and unreliable.
Creating an ON INSERT on ON UPDATE trigger it the correct solution, then in the trigger you can put the new or updated data into a separate table which you can query. Triggers are the tool to monitor changes in the database without doing anything in the applications using them.
Example trigger for 'after update' on table MY_TABLE(id, name):
CREATE TRIGGER mark_changes AFTER UPDATE ON my_table FOR EACH ROW
BEGIN
INSERT INTO tracking_table VALUES "Change in table my_table", OLD.id, NEW.id
END$$
This assumes you have a table tracking_table(description, old_id, new_id)