SQL Server insert trigger not working - sql

This is my first time using triggers.
My trigger is not being triggered please help.
CREATE TRIGGER sbhack_autoban
ON LOG_CONNECT201211
FOR INSERT
AS
BEGIN
/* query to run if single or multiple data is
inserted into LOG_CONNECT201211 table */
UPDATE login.dbo.USER_CHECK_LOGIN
SET login.dbo.USER_CHECK_LOGIN.CHECKLOGIN = 2
WHERE login.dbo.USER_CHECK_LOGIN.USER_KEY IN
(SELECT e.USER_KEY
FROM game.dbo.CHAR_DATA0 AS e
INNER JOIN gamelogs.dbo.LOG_USING_DEPOT201211 AS p
ON e.CHAR_KEY = p.CHAR_KEY
WHERE p.GATENUM = 150)
AND login.dbo.USER_CHECK_LOGIN.CHECKLOGIN = 0
AND login.dbo.USER_CHECK_LOGIN.USER_KEY != 51;
END
This is suppose to run the query inside the BEGIN : END if an entry is inserted into the LOG_CONNECT201211 table. But nothing is happening even when I have inserted multiple data into LOG_CONNECT201211.

When your INSERT trigger fires - then at least one new row has been inserted! That's a fact.
Now the question is: given that a single or multiple new rows have been inserted - what do you want to do with this knowledge??
Typically, you could e.g. set a column to a value you cannot specify as a default constraint - or you could insert the fact that the row has been inserted into an audit table or something....
So you'd have something like this:
CREATE TRIGGER sbhack_autoban
ON LOG_CONNECT201211
FOR INSERT
AS
INSERT INTO LogAudit(InsertedDate, UserKey)
SELECT
GETDATE(), i.User_Key
FROM
Inserted i
or something like that....
Update: ok, so you want to run that UPDATE statement when the rows have been inserted - not 100% clear, what columns/values from the inserted rows you want to use - looks like the e.UserKey column only - correct?
Then the UPDATE would be:
UPDATE login.dbo.USER_CHECK_LOGIN
SET login.dbo.USER_CHECK_LOGIN.CHECKLOGIN = 2
WHERE
login.dbo.USER_CHECK_LOGIN.USER_KEY IN
(SELECT USER_KEY FROM Inserted)
AND login.dbo.USER_CHECK_LOGIN.CHECKLOGIN = 0
AND login.dbo.USER_CHECK_LOGIN.USER_KEY != 51;
Update #2:
The point I still don't understand is : why do you want to run an update that uses the USER_CHECK_LOGIN, CHAR_DATA0 and LOG_USING_DEPOT201211 tables, when some rows are getting inserted into a totally separate, unrelated table LOG_CONNECT201211 ??
A trigger is used when you want to do something because rows have been inserted into that table - but in that case, you typically want to do something with the rows and their values that have been inserted...
I just don't see any connection between the rows being inserted into LOG_CONNECT201211 event, and the tables you are then querying from and updating. Where's the link?? WHY do you need to run *this UPDATE when data is inserted into LOG_CONNECT201211 ?? It would make sense if data where inserted into one of the tables involved in the UPDATE - but like this, it just totally doesn't make any sense .....

Related

Trigger: How does the inserted table work? How to access its rows?

I have the following table
Data --Table name
ID -- Identity column
PCode -- Postal Code
I created the following trigger:
CREATE TRIGGER Trig
ON Data
FOR INSERT
AS
BEGIN
Select * from inserted
END
And inserted the following values
INSERT INTO Data VALUES (125)
INSERT INTO Data VALUES (126)
INSERT INTO Data VALUES (127)
It shows this:
But I was expecting something like this:
After the 1st insertion, the trigger is executed -> one row is shown in the inserted table.
After the 2nd insertion, the trigger is executed -> two rows are shown in the inserted table.
After the 3rd insertion, the trigger is executed -> three rows are shown in the inserted table.
According to msdn.microsoft all the rows inserted are in this table.
How can I access the inserted table so that I can see all the expected rows and not separately?
You can not. From the Use the inserted and deleted Tables article on microsoft.com, you can read:
The inserted table stores copies of the affected rows during INSERT and UPDATE statements.
That means that the inserted table will only contain rows for the current INSERT or UPDATE statement.
If you do want to see all rows for several such INSERT or UPDATE statements, you will have to store these rows in a table you created yourself.
There are 2 table available in a trigger, the inserted and the deleted. Each update on table XXX is actually a delete row X from XXX then an insert of row X in table XXX. So the inserted inside the trigger is a copy of what got inserted. You can do a lot with a trigger, but triggers are dangerous.
For example, on a performance gig, I found a huge SP being run by a trigger, we dropped it and the database came back online. Or another example, if you do a trigger wrong to audit logins, you can down the server.
As TT mentioned, if you want to see all the inserted records then you need to change your Trigger to something like this:
CREATE TRIGGER Trig
ON Data
FOR INSERT
AS
BEGIN
Select * into "tablename"
from
(Select * from inserted) Ins
END

SQL Server : make update trigger don't activate with no changing value

I want to track the update changes in a table via a trigger:
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted
However in real production some of the update queries select rows with vague conditions and update them all regardless of whether they are actually changed, like
UPDATE Targettable
SET customer_type = 'VIP'
WHERE 1 = 1
--or is_obsolete = 0 or register_date < '20160101' something
But due to table size and to analyze, I only want to choose those actually modified data for tracking. How to achieve this goal?
My track table has many columns (so I do not prefer checking inserted and deleted column one by one) but it seldom changes structure.
I guess the following code will be useful.
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT *
FROM Inserted
EXCEPT
SELECT *
FROM Deleted
I realize this post is a couple months old now, but for anyone looking for a well-rounded answer:
To exit the trigger if no rows were affected on SQL Server 2016 and up, Microsoft recommends using the built-in ROWCOUNT_BIG() function in the Optimizing DML Triggers section of the Create Trigger documentation.
Usage:
IF ROWCOUNT_BIG() = 0
RETURN;
To ensure you are excluding rows that were not changed, you'll need to do a compare of the inserted and deleted tables inside the trigger. Taking your example code:
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted i
INNER JOIN deleted d
ON d.[SomePrimaryKeyCol]=i.[SomePrimaryKeyCol] AND
i.customer_type<>d.customer_type
Microsoft documentation and w3schools are great resources for learning how to leverage various types of queries and trigger best practices.
Prevent trigger from doing anything if no rows changed.
Writing-triggers-the-right-way
CREATE TRIGGER the_trigger on dbo.Data
after update
as
begin
if ##ROWCOUNT = 0
return
set nocount on
/* Some Code Here */
end
Get a list of rows that changed:
CREATE TRIGGER the_trigger on dbo.data
AFTER UPDATE
AS
SELECT * from inserted
Previous stack overflow on triggers
#anna - as per #Oded's answer, when an update is performed, the rows are in the deleted table with the old information, and the inserted table with the new information –

Sql Trigger After Insert Update another table with conditions

I am creating After Insert trigger , its working fine, but I have certain conditions before executing the statements inside the trigger
Based on Different CustomerId Run the trigger, I want check which CustomerId got inserted in my LoyaltyDetailsTable, say if last insert
was Customerid=2 then pass that Customerid in where condition then run
the trigger , or if Customerid = 1 then run the trigger for that Id,
so on.
I want to check whether in PriceClaimTable the inserted CustomerId exist or not, If exists then update the details else just insert the
values in LoyaltyDetailsTable only.
Trigger query
CREATE TRIGGER DetailsAfterInsert ON [dbo].[LoyaltyDetailsTable]
FOR INSERT
as
UPDATE PriceClaimTable
SET CurrentPoints =
(
(SELECT SUM(LoayaltyPointsTable.Points) AS RecentPoints FROM LoayaltyPointsTable
join LoyaltyDetailsTable ON LoayaltyPointsTable.LoyaltyPointsId
= LoyaltyDetailsTable.LoyaltyPointsId
WHERE CustomerId=1 and LoyaltyDetailsId= (SELECT MAX(LoyaltyDetailsId)
AS LoyaltyDetailsTable FROM LoyaltyDetailsTable))
+
(SELECT CurrentPoints FROM PriceClaimTable WHERE ClaimCustomerId=1 and
PriceClaimId=(SELECT max(PriceClaimId) FROM PriceClaimTable
))
)
WHERE ClaimCustomerId=1 and PriceClaimId=(SELECT max(PriceClaimId) FROM PriceClaimTable)
This is my first attempt to write a trigger, and here is table structure.
Any help would be great.
What you're looking for here is the inserted table. Every time you issue an UPDATE statement, SQL Server generates two virtual tables called inserted and deleted that store information on the data modifications you're making. These tables are accessible from your trigger. For more information, see here: https://msdn.microsoft.com/en-us/library/ms191300.aspx
You can use inserted to get the IDs you're looking for. So, instead of:
WHERE ClaimCustomerId=1
you can use:
WHERE ClaimCustomerId=inserted.ClaimCustomerId

Insert trigger will not work properly

I am trying to create a database trigger that will update certain characters in a field for a table when a user inserts data into the table...
Ex.
ID EXCHANGE LEADRT
1 new L-3
2 new 3
3 new 5
So I would want to leave id 1 alone because the format for the LEADRT is correct but ids 2 and 3 are not.
CREATE TRIGGER triggerupdate ON PoleUnits FOR INSERT,
UPDATE AS
if not exists (select * from Poleunits where LEADRT like '%L-%')
update PoleUnits set LEADRT = STUFF (LEADRT, 1, 0,'L-');
Any ideas why I can't get this to work or better suggestions on how to accomplish this?
In insert and update triggers you have access to a specific table called inserted where the rows to be inserted/updated are held. Those are not real tables, they are just logical tables with the same structure as the table on which the trigger fired.
Your current logic works on the original table, thus working with all the existing data, but not with the data you are actually inserting, i.e. it will update everything except the data you actually want updated. Something like this could work:
CREATE TRIGGER triggerupdate ON PoleUnits
FOR INSERT, UPDATE AS
update PoleUnits
set LEADRT = STUFF (PoleUnits.LEADRT, 1, 0,'L-')
from PoleUnits
inner join inserted -- this is basically a self join
on PoleUnits.ID = inserted.ID
where PoleUnits.LEADRT not like '%L-%'
This will only update those rows in PoleUnits that are being inserted, and only if their LEADRT field is not in the L- format.

SQL Triggers - Deleted or Updated? or maybe something else?

I am trying to figure out which i need to use here: deleted, inserted or updated.
basically.
I need to write some data to the history table, when the main table is updated, and only if the status changes from something to either pending or active.
This is what I have now:
ALTER TRIGGER [dbo].[trg_SourceHistory] ON [dbo].[tblSource]
FOR UPDATE AS
DECLARE #statusOldValue char(1)
DECLARE #statusNewValue char(1)
SELECT #statusOldValue = statusCode FROM deleted
SELECT #statusNewValue= statusCode FROM updated
IF (#statusOldValue <> #statusNewValue) AND
(#statusOldValue = 'P' or #statusOldValue = 'A')
BEGIN TRY
INSERT * INTO tblHistoryTable)
select * from [DELETED]
so I want the new data to stay in the main table, the the history table to be updated with what is being overwritten... right now it just copies the same info over. so after update, both my tables have the same data.
There are only the Inserted and Deleted pseudo tables - there's no Updated.
For an UPDATE, Inserted contains the new values (after the update) while Deleted contains the old values before the update.
Also be aware that the triggers is fired once per batch - not once for each row. So both pseudo tables will potentially contain multiple rows! Don't just assume a single row and assign this to a variable - this
SELECT #statusOldValue = statusCode FROM deleted
SELECT #statusNewValue= statusCode FROM updated
will fail if you have multiple rows ! You need to write your triggers in such a fashion that they work with multiple rows in Inserted and Deleted !
Update: yes - there IS a much better way to write this:
ALTER TRIGGER [dbo].[trg_SourceHistory] ON [dbo].[tblSource]
FOR UPDATE
AS
INSERT INTO dbo.tblHistoryTable(Col1, Col2, Col3, ...., ColN)
SELECT Col1, COl2, Col3, ..... ColN
FROM Deleted d
INNER JOIN Inserted i ON i.PrimaryKey = d.PrimaryKey
WHERE i.statusCode <> d.statusCode
AND d.statusCode IN ('A', 'P')
Basically:
explicitly specify the columns you want to insert - both in the INSERT statement as well as the SELECT statement retrieving the data to insert - to avoid any nasty surprises
create an INNER JOIN between Inserted and Deleted pseudo-tables to get all rows that were updated
specify all other conditions (different status codes etc.) in the WHERE clause of the SELECT
This solution works for batches of rows being updated - it won't fail on a multi-row update....
You need to use both the inserted and deleted tables together to check for records that:
1. Already existed (to check it's not an insert)
2. Still exists (to check it's not a delete)
3. The Status field changed
You also need to make sure you do that in a set based approach, as per marc_s's answer, triggers are not single record processes.
INSERT INTO
tblHistoryTable
SELECT
deleted.*
FROM
inserted
INNER JOIN
deleted
ON inserted.PrimaryKey = deleted.PrimaryKey
WHERE
inserted.StatusCode <> deleted.StatusCode
AND (inserted.StatusCode = 'P' OR inserted.StatusCode = 'A')
inserted = the new values
deleted = the old values
There is no updated table, you are looking for inserted.