sql server trigger - sql

I have a table structure like this:
create table status_master
(
Name varchar(40)
status varchar(10)
)
I need to create trigger for status column if the status column value updated value
FAIL then the trigger invoke one insert commant like:
insert into temp value('s',s's')
Could you please any one give me tha idea to solve this?

Not sure what you really want to achieve - but in SQL Server, you have two types of triggers:
AFTER triggers that fire after INSERT, UPDATE, DELETE
INSTEAD OF triggers which can catch the operation (INSERT, UPDATE, DELETE) and do something instead
SQL Server does not have the BEFORE INSERT/UPDATE/DELETE triggers that other RDBMS have.
You can have any number of AFTER triggers, but only one INSTEAD OF trigger for each operation (INSERT, UPDATE, DELETE).
The more common case is the AFTER trigger, something like:
CREATE TRIGGER trgCheckInsertedValues
ON status_master
AFTER INSERT
AS
BEGIN
INSERT INTO dbo.temp(field1, field2, field3)
SELECT i.Name, i.Status
FROM inserted i
WHERE i.Status = 'FAIL'
END
Here, I am inspecting the "inserted" pseudo-table which contains all rows inserted into your table, and for each row that contains "status = FAIL", you'd be inserting some fields into a "temp" table.
Again - not sure what you really want in detail - but this would be the rough outline how to do it in SQL Server T-SQL code.
Marc

Trigger in SQL, is used to trigger a query when any action perform in the particular table like insert,delete,update
http://allinworld99.blogspot.com/2015/04/triggers-in-sql.html

What you're looking for is an INSTEAD OF INSERT, UPDATE trigger. Within your trigger you attempt the insert or update yourself inside a try-catch. If it errors out then you insert those values into your other table (assuming it's a logging table of some sort).

Assuming what you mean is, should the status's new value be FAIL, then what about this:
triggers reference the new record row as 'inserted' and the old one as 'deleted'
CREATE TRIGGER trgCheckInsertedValues ON status_master AFTER INSERT AS
BEGIN
if inserted.status = 'FAIL'
INSERT INTO dbo.temp(field1, field2, field3)
SELECT i.Name, i.Status, 'anything' FROM inserted i

Related

How to do these three things in a SQL Server transaction - 1. create table, 2.create trigger on table, 3. select from another table

I am trying to accomplish the following 3 simple tasks as a transaction (i.e. I need to lock old_table and new_table until the process completes).
Create a new table (new_table)
Add a trigger to old_table, which queues updates to new_table.
Select all the data from old_table and return it.
Note that I want these handled in a single transaction. I cannot allow inserts into old_table (and therefore triggered inserts into new_table) in between the trigger creation and the select on old_table.
My current closest attempt is this, but truthfully I feel that I am very far off from accomplishing my goal with this code. I have added the code just for reference of what I am trying, but I am mostly interested in non-specific answers that layout how to accomplish the above three comands in a transaction.
DROP PROCEDURE IF EXISTS dbo.BuildAll;
CREATE PROCEDURE dbo.BuildAll
AS
BEGIN
BEGIN TRANSACTION
DECLARE #TriggerCode VARCHAR(MAX)
CREATE TABLE dbo.new_table
(
status nvarchar(5),
type char(1),
col1 nvarchar(50),
col2 smallint
)
SELECT #TriggerCode = 'CREATE TRIGGER myTrigger
ON dbo.old_table FOR INSERT
AS
DECLARE #col1_new nvarchar(50)
DECLARE #col2_new smallint
SELECT #col1_new = col1 FROM inserted
SELECT #col2_new = col2 FROM inserted
IF #col1_new IS NOT NULL
BEGIN
INSERT INTO new_table (status, type, col1, col2)
SELECt "Q", "A", #col1, #col2 FROM inserted
END'
EXEC(#TriggerCode)
SELECT * FROM old_table
COMMIT
END
Going to suggest this an a possible solution you can try. This doesn't address the correctness of your actual trigger, you have two separate questions here really.
You don't need to encapsulate this entire process in a transaction.
Create your new table.
Create your trigger on old table, but disabled.
set transaction isolation level serializable
begin tran
go
create trigger <Name> on <Table> etc
go
disable trigger <Name> on <Table>
go
commit
Now in a transaction you can lock the old table against other activity while you work
begin tran
update oldtable with(tablockx) set column=column where id=0 /* block other processes from updating table, id=0 row doesn't exist */
query your data and process as required
enable trigger <Name> on <Table>
commit
This trigger code of yours is kinda odd .... you have a trigger on all three operations - yet it appears as if you're never using the values you fetch from the deleted pseudo table, and if the value from the inserted table is NULL, you're not doing anything inside your trigger - so you can really spare yourself the DELETE case - that'll never do anything....
Also, as mentioned in my comment - you Inserted pseudo table can easily contain multiple rows - but you're selecting from it as if you only ever expect it to contain a single row.
You should really rewrite your trigger code to handle the case of multiple rows in Inserted and make the whole thing properly set-based - something like this:
CREATE TRIGGER myTrigger
ON dbo.old_table
FOR INSERT, UPDATE
AS
INSERT INTO new_table (status, type, col1, col2)
SELECT 'Q', 'A', i.col1, i.col2
FROM Inserted i
Whether you need this on the UPDATE case at all - I cannot tell, you need to decide this. But basically: just select from the Inserted table, take the Col1 and Col2 values, and add the constant values 'Q' and 'A' to your insert to handle multiple rows properly. That should do it.

How to detect if a trigger is fired for an insert or update?

I need to make a table in my database that has all of the history of all DML statements that occur in a given table. The LOG table is like this:
id
event
ts
record
1
update
2020-01-01
record1
2
delete
2020-01-02
record2
LOG table has a sequence for auto-incrementing the ID column and TS default is NOW.
Do I need to create a sequence for every statement first or I can just create triggers for AFTER a DML statement is fired?
CREATE TRIGGER UPDATE
ACTIVE AFTER INSERT OR UPDATE
ON TableA
AS
BEGIN
INSERT INTO LOG (event,record)
SELECT record FROM TableA; # what I need to add here to set the event = UPDATE,INSERT or DELETE?
END
Can I use something like this in my triggers with a statement to add an event values?
EDIT: This is the working solution thanks to #Mark Rotteveel and #Arioch'The:
CREATE TRIGGER INSERT_UPDATE FOR TableA
ACTIVE AFTER INSERT OR UPDATE
AS
BEGIN
IN AUTONOMOUS TRANSACTION DO
INSERT INTO LOG(event,record)
VALUES (CASE WHEN inserting THEN 'INSERTED' WHEN updating THEN 'UPDATED' END, new.record);
END
To detect the type of event, you can use the INSERTING or UPDATING (or DELETING) context variables.
As an aside, do not use SELECT record FROM TableA in a trigger on TableA, instead use the NEW context (or OLD for a delete).
The solution would look something like this:
insert into LOG (event, record)
values (case when :inserting then 'insert' when :updating then 'update' end, new.record);

SQL trigger to add new records to a table with the same structure when an insertion is made

I am trying to create SQL trigger which adds a new record to the same table where an insertion is made through a web page. I am not exactly sure how to implement it but I tried the following query
CREATE trigger [dbo].[trgI_DealsDoneInserRecord]
on [dbo].[Terms]
after insert
As
Insert into DealsDone
(Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product)
VALUES
(SELECT Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product FROM inserted)
END
The above query threw an error in the SELECT statement in VALUES.
May I know a way to implement this?
Try this:
CREATE trigger [dbo].[trgI_DealsDoneInserRecord]
ON [dbo].[Terms]
AFTER INSERT
As
BEGIN
INSERT INTO DealsDone
(Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product)
SELECT Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product FROM inserted
END
While I generally advocate against using SELECT *, in this case it seems like a benefit:
By not specifying the fields you can automatically account for changes in the tables without having to update this trigger if you add or remove or even rename fields.
This will help you catch errors in schema updates if one of the tables is updated but the other one isn't and the structure is then different. If that happens, the INSERT operation will fail and you don't have to worry about cleaning up bad data.
So use this:
CREATE TRIGGER [dbo].[trgI_DealsDoneInserRecord]
ON [dbo].[Terms]
AFTER INSERT
AS
SET NOCOUNT ON;
INSERT INTO [DealsDone]
SELECT *
FROM inserted;
There is an syntax issue, and also you are missing BEGIN
The basic syntax is
INSERT INTO table2 (column_name(s))
SELECT column_name(s)
FROM table1;
So try this
CREATE trigger [dbo].[trgI_DealsDoneInserRecord]
on [dbo].[Terms]
after insert
As
BEGIN
Insert into DealsDone
(Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product)
SELECT Company,Grade,Term,Pipeline,[Index],Volume,Price,[Type],CounterParty,
TermID,GradeID,CPID,Locked,Product
FROM inserted
END
Refer:- http://technet.microsoft.com/en-us/library/ms188263(v=sql.105).aspx

Forbid insert into table on certain conditions

I have a SQL Server 2008 database. There are three terminals connected to it (A, B, C). There is a table SampleTable in the database, which reacts to any terminal activity. Every time there is some activity on any terminal, logged on to this DB, the new row is inserted into SampleTable.
I want to redirect traffic from one (C) of the three terminals to write to table RealTable and not SampleTable, but I have to do this on DB layer since services that write terminal activity to DB are in Black Box.
I already have some triggers working on SampleTable with the redirecting logic, but the problem is that rows are still being inserted into SampleTable.
What is the cleanest solution for this. I am certain that deleting rows in an inserting trigger is bad, bad, bad.
Please help.
Edit:
Our current logic is something like this (this is pseudo code):
ALTER TRIGGER DiffByTerminal
ON SampleTable
AFTER INSERT
AS
DECLARE #ActionCode VARCHAR(3),
#ActionTime DATETIME,
#TerminalId INT
SELECT #ActionCode = ins.ActionCode,
#ActionTime = ins.ActionTime,
#TerminalId = ins.TerminalId
FROM inserted ins
IF(#TerminalId = 'C')
BEGIN
INSERT INTO RealTable
(
...
)
VALUES
(
#ActionCode,
#ActionTime,
#TerminalId
)
END
In order to "intercept" something before a row gets inserted into a table, you need an INSTEAD OF trigger, not an AFTER trigger. So you can drop your existing trigger (which also included flawed logic that assumed all inserts would be single-row) and create this INSTEAD OF trigger instead:
DROP TRIGGER DiffByTerminal;
GO
CREATE TRIGGER dbo.DiffByTerminal
ON dbo.SampleTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.RealTable(...) SELECT ActionCode, ActionTime, TerminalID
FROM inserted
WHERE TerminalID = 'C';
INSERT dbo.SampleTable(...) SELECT ActionCode, ActionTime, TerminalID
FROM inserted
WHERE TerminalID <> 'C';
END
GO
This will handle single-row inserts and multi-row inserts consisting of (a) only C (b) only non-C and (c) a mix.
One of the easiest solution for you is INSTEAD OF trigger. Simply stating, it's trigger that "fires" on very action you decide and lets you "override" the default behavior of the action.
You can override the INSERT, DELETE and UPDATE statements for specific table/view (you use it a lot with views that combine data from different tables and you want make the view insert-able) using INSTEAD OF trigger, where you can put your logic. inside the trigger you can then call again to INSERT when it's appropriate, and you don't have to worry about recursion - INSTEAD OF triggers won't apply on statements from inside the trigger code itself.
Enjoy.

How can I do a BEFORE UPDATED trigger with sql server?

I'm using Sqlserver express and I can't do before updated trigger. There's a other way to do that?
MSSQL does not support BEFORE triggers. The closest you have is INSTEAD OF triggers but their behavior is different to that of BEFORE triggers in MySQL.
You can learn more about them here, and note that INSTEAD OF triggers "Specifies that the trigger is executed instead of the triggering SQL statement, thus overriding the actions of the triggering statements." Thus, actions on the update may not take place if the trigger is not properly written/handled. Cascading actions are also affected.
You may instead want to use a different approach to what you are trying to achieve.
It is true that there aren't "before triggers" in MSSQL. However, you could still track the changes that were made on the table, by using the "inserted" and "deleted" tables together. When an update causes the trigger to fire, the "inserted" table stores the new values and the "deleted" table stores the old values. Once having this info, you could relatively easy simulate the "before trigger" behaviour.
Can't be sure if this applied to SQL Server Express, but you can still access the "before" data even if your trigger is happening AFTER the update. You need to read the data from either the deleted or inserted table that is created on the fly when the table is changed. This is essentially what #Stamen says, but I still needed to explore further to understand that (helpful!) answer.
The deleted table stores copies of the affected rows during DELETE and
UPDATE statements. During the execution of a DELETE or UPDATE
statement, rows are deleted from the trigger table and transferred to
the deleted table...
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table...
https://msdn.microsoft.com/en-us/library/ms191300.aspx
So you can create your trigger to read data from one of those tables, e.g.
CREATE TRIGGER <TriggerName> ON <TableName>
AFTER UPDATE
AS
BEGIN
INSERT INTO <HistoryTable> ( <columns...>, DateChanged )
SELECT <columns...>, getdate()
FROM deleted;
END;
My example is based on the one here:
http://www.seemoredata.com/en/showthread.php?134-Example-of-BEFORE-UPDATE-trigger-in-Sql-Server-good-for-Type-2-dimension-table-updates
sql-server triggers
T-SQL supports only AFTER and INSTEAD OF triggers, it does not feature a BEFORE trigger, as found in some other RDBMSs.
I believe you will want to use an INSTEAD OF trigger.
All "normal" triggers in SQL Server are "AFTER ..." triggers. There are no "BEFORE ..." triggers.
To do something before an update, check out INSTEAD OF UPDATE Triggers.
To do a BEFORE UPDATE in SQL Server I use a trick. I do a false update of the record (UPDATE Table SET Field = Field), in such way I get the previous image of the record.
Remember that when you use an instead trigger, it will not commit the insert unless you specifically tell it to in the trigger. Instead of really means do this instead of what you normally do, so none of the normal insert actions would happen.
Full example:
CREATE TRIGGER [dbo].[trig_020_Original_010_010_Gamechanger]
ON [dbo].[T_Original]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Old_Gamechanger int;
DECLARE #New_Gamechanger int;
-- Insert statements for trigger here
SELECT #Old_Gamechanger = Gamechanger from DELETED;
SELECT #New_Gamechanger = Gamechanger from INSERTED;
IF #Old_Gamechanger != #New_Gamechanger
BEGIN
INSERT INTO [dbo].T_History(ChangeDate, Reason, Callcenter_ID, Old_Gamechanger, New_Gamechanger)
SELECT GETDATE(), 'Time for a change', Callcenter_ID, #Old_Gamechanger, #New_Gamechanger
FROM deleted
;
END
END
The updated or deleted values are stored in DELETED. we can get it by the below method in trigger
Full example,
CREATE TRIGGER PRODUCT_UPDATE ON PRODUCTS
FOR UPDATE
AS
BEGIN
DECLARE #PRODUCT_NAME_OLD VARCHAR(100)
DECLARE #PRODUCT_NAME_NEW VARCHAR(100)
SELECT #PRODUCT_NAME_OLD = product_name from DELETED
SELECT #PRODUCT_NAME_NEW = product_name from INSERTED
END