TSQL Trigger unknown Column - sql-server

I have a table which have 31 Columns
PrimaryField,PersNr, a1, a2, a3, a4, ...
in the a1 - a31 fields are values. The software generate automaticly each month one row.
The user can now from the software update a field or delete it (Update to NULL).
But now I need a Trigger which make an insert in a new table for each changed field. The Problem is the comparison from the before row and the changed row. How can I get the changed field when the User Update the Row?

Here is some code you can run that should work:
CREATE TRIGGER TriggerName --The name of your trigger
ON Table1 --The table where the update happens
AFTER UPDATE
AS
INERT INTO --you new tabe namegoes here
()--your new table fields go here
SELECT --only the fields you have in the parenthesis above in the same order
FROM Inerted I
The way most triggers are is that as soon as a value is inseted updated or deleted the trigger has a special vlaue in this case fr update and inserted actions the table is called Inserted and that will grab the most current value entered.

In a SQL Server trigger, you have access to Inserted and Deleted pseudo tables; those keep the data that has been modified: the first one stores the new values, the second the old ones.
CREATE TRIGGER [dbo].[MyTableUpdate
ON [dbo].[MyTable]
AFTER UPDATE
AS
IF ##ROWCOUNT = 0
RETURN
IF NOT UPDATE (a1) AND NOT UPDATE(a2) AND NOT UPDATE(a3) AND NOT UPDATE...
RETURN
SELECT YourCOlumns FROM Inserted
SELECT YourColumsn FROM Deleted
--Compare the values as you prefer

Thank you all for helping me.
In the morning when I stand up I got an Idea :)
The point was:
1. DECLARE AND Set Variables:
DECLARE #ai1 VARCHAR(30);
DECLARE #ad1 VARCHAR(30);
....
SET #ai1 = (SELECT a1 from inserted);
SET #ad1 = (SELECT a1 from deleted);
...
To Compare every #ai and #ad
IF NOT #ai1 = #ad1 OR (#ai1 IS NULL AND #ad1 IS NOT NULL) OR (#ai1 IS NOT NULL AND #ad1 IS NULL)
BEGIN
... Insert...
END
IF NOT #ai2 = #ad2...
Thats the Trick. But thank you all very much!!!

Related

SQL - trigger insert only when a user made an update

I have a table, customers_accounts, that tracks some basic information about a customer on an account. When the customer's file is opened, I synchronize the information from an external system so our user gets the most updated information.
UPDATE
customers_accounts
SET
first_name = 'bob',
last_name = 'burger'
WHERE
account_number = '12345'
When a user updates the account, I do the same query, but I update a column indicating the last user to make the change
UPDATE
customers_accounts
SET
first_name = 'bob',
last_name = 'burger',
updated_by = 'H Jon Benjamin',
updated_on = GETDATE()
WHERE
account_number = '12345'
Here's the problem I'm trying to solve. I want to track changes in a history table, but only log changes when they're made by a user, not if they're from the external system. So my plan was to create a trigger that inserts a row if the user column is not blank on the insert (since the updated_by is implicitly null above in the first update)
What I tried is this:
ALTER trigger [dbo].[Accounts_Customers_LogUpdate]
ON [dbo].[Accounts_Customers]
AFTER UPDATE
AS
DECLARE #Now AS DATETIME = GETDATE()
DECLARE #User AS NVARCHAR(150)
SELECT #User = (SELECT [updated_by] FROM INSERTED)
IF (#User IS NOT NULL)
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Accounts_Customers-History]
SELECT *, #User, #Now
FROM inserted
END
Accounts_Customers-History is an exact copy of the table with two additional columns, change_made_by and change_made_on
It doesn't behave how i'd expect though. It inserts whatever value is in updated_by into change_made_by, regardless of the value of updated_by in the query. So I'm getting logged activity triggered by both the user and the import.
Use UPDATE() for this:
Returns a Boolean value that indicates whether an INSERT or UPDATE attempt was made on a specified column of a table or view. UPDATE() is used anywhere inside the body of a Transact-SQL INSERT or UPDATE trigger to test whether the trigger should execute certain actions.
This means that the update function will return false for the first update statement in the question, and true for the second update statement - which is exactly what you need.
Also, please note you should always specify the columns list in an insert statement,
and also always specify the columns list in a select statement. (Why?)
A revised version of your trigger might look something like this:
ALTER TRIGGER [dbo].[Accounts_Customers_LogUpdate]
ON [dbo].[Accounts_Customers]
AFTER UPDATE
AS
DECLARE #Now as DateTime = GetDate()
IF UPDATE(updated_by)
BEGIN
-- Always specify the columns list in an insert statement!
insert into [dbo].[Accounts_Customers-History] (<Columns list>)
-- Always specify the columns list in a select statement!
select <columns list>, #Now
from inserted
END
Please note that the UPDATE() function does not give you any indication if the insert or update statement that fired the trigger was successful, nor does it give you an indication if the value of the column has actually changed - it only indicates whether that column was a part of the insert or update statement that fired the trigger - as you can read in the last paragraph of the remarks section:
If a trigger applies to a column, the UPDATED value will return as true or 1, even if the column value remains unchanged. This is by-design, and the trigger should implement business logic that determines if the insert/update/delete operation is permissible or not.

T-SQL Increment Id after Insert

I'm currently working on a stored procedure in SQL Server 2012 using T-SQL. My problem: I have several SWOTs (e.g. for a specific client) holding several SWOTParts (strengths, weaknesses, opportunities, and threats). I store the values in a table Swot as well as in another table SwotPart.
My foreign Key link is SwotId in SwotPart, thus 1 Swot can hold N SwotParts. Hence, I store the SwotId in every SwotPart.
I can have many Swots and now need to set the SwotId correctly to create the foreign key. I set the SwotId using SCOPE_IDENTITY() unfortunately it only takes the last SwotId from the DB.I'm looking for something like a for loop to increment the SwotId after each insert on the 1st insert.
DECLARE #SwotId INT = 1;
-- 1st insert
SET NOCOUNT ON
INSERT INTO [MySchema].[SWOT]([SwotTypeId]) // Type can be e.g. a sepcific client
SELECT SwotTypeId
FROM #SWOTS
SET #SwotId = SCOPE_IDENTITY(); // currently e.g. 7, but should increment: 1, 2, 3...
-- 2nd insert
SET NOCOUNT ON
INSERT INTO [MySchema].[SwotPart]([SwotId], [FieldTypeId], [Label]) // FieldType can be e.g. Streangh
SELECT #SwotId, FieldTypeId, Label
FROM #SWOTPARTS
Do you know how to solve this issue? What could I use instead of SCOPE_IDENTITY()?
Thank you very much!
You can output the inserted rows into a temporary table, then join your #swotparts to the temporary table based on the natural key (whatever unique column set ties them together beyond the SwotId). This would solve the problem with resorting to loops or cursors, while also overcoming the obstacle of doing a single swot at a time.
set nocount, xact_abort on;
create table #swot (SwotId int, SwotTypeId int);
insert into MySchema.swot (SwotTypeId)
output inserted.SwotId, inserted.SwotTypeId into #swot
select SwotTypeId
from #swots;
insert into MySchema.SwotPart(SwotId, FieldTypeId, Label)
select s.SwotId, p.FieldTypeId, p.Label
from #swotparts p
inner join #swot s
on p.SwotTypeId = p.SwotTypeId;
Unfortunately I cant comment so I`ll leave you an answer hopefully to clarify some things:
Since you need to create the correct foreign key I don`t understand
why do you need to increment a value instead of using the id inserted
into the SWOT table.
I suggest returning the inserted id using the SCOPE_IDENTITY right after the insert statement and use it for you insert into the swot parts (there is plenty of info about it and how to use it)
DECLARE #SwotId INT;
-- 1st insert
INSERT INTO [MySchema].[SWOT]([SwotTypeId]) // Type can be e.g. a sepcific client
SET #SwotId = SCOPE_IDENTITY();
-- 2nd insert
INSERT INTO [MySchema].[SwotPart]([SwotId], [FieldTypeId], [Label])
SELECT #SwotId, FieldTypeId, Label
FROM #SWOTPARTS

How to store existing column value in new table using trigger

I have two tables
Customer
CustomerUpdate
Structure of both tables are like this
Customer table's structure
CustomerName | CustomerId
CustomerUpdate table's structure
NewCustomerName | NewCustomerId | OldCustomerName
I have few values inserted in the Customer table. Whenever I should update the data in this table I want that the existing as well as new data should be triggered into new table CustomerUpdate.
For this I created a trigger but this is only pulling the updated data, it's not pulling the existing data..
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
SET NOCOUNT ON
declare #NewCustomerName nchar(20);
declare #NewCustomerId nchar(20);
declare #OldCustomerName nchar(20);
declare #audit_action varchar(100);
select #NewCustomerName = i.CustomerName from inserted i;
select #NewCustomerId = i.CustomerId from inserted i;
select #OldCustomerName = c.CustomerName
from Customer c
where CustomerId = #NewCustomerId;
if update(CustomerName)
set #audit_action='Updated Record -- After Update Trigger.';
if update(CustomerId)
set #audit_action='Updated Record -- After Update Trigger.';
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
values(#NewCustomerName, #NewCustomerId, #OldCustomerName);
PRINT 'AFTER UPDATE Trigger fired.'
GO
Please help me out
First, selecting from the table being modified when an update trigger is executing will get the new value. These are AFTER triggers (rather than INSTEAD triggers) and therefore the update has already happened by the time the trigger fires (although it can be rolled back). If you need the old value, you should select from the DELETED pseudo-table.
Second, as pointed out by #marc_s in comments, your trigger has the hidden assumption that only one row is affected by each update. This may very well be a valid assumption for your environment, if your application only ever updates one row at a time, but in the general case, every trigger should be ready to handle the case where many rows are affected by a single update. Writing your triggers to handle multiple rows is good practice.
Third, all of your sequentially executing code is pretty much unnecessary. The old value and the new value can be retrieved and inserted all at once:
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
-- case 1: ID unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerID=D.CustomerID
UNION ALL
-- case 2: ID changed, Name unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerName=D.CustomerName
WHERE I.CustomerID<>D.CustomerID
UNION ALL
--case 3: ID changed, Name changed
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
LEFT JOIN Deleted D on I.CustomerID=D.CustomerID OR I.CustomerName=D.CustomerName
WHERE D.CustomerID IS NULL;
END

One table return value to insert another table as a column Oracle

I have a Oracle trigger and I need to create column after insert rows to first table.
So.. In my scenario:
When some record inserted into NEWS_TBL and i need to get that(in here i get it via last inserted record) and i need to get the NAME from the NEWS_TBL and returning value to NewsName variable and that returned value inserted to the NEWS_TYPE_TBL as a Column.
Below code is not working. can anyone pls give me a solution for this.
MyCode
BEFORE DELETE OR INSERT OR UPDATE
ON NEWS.NEWS_FIRST
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
NewsName varchar2(50);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE
'select *
from ( select a.NAME,a.ID, max(ID) over () as max_pk
from NEWS_TBL a)
where ID = max_pk
RETURNING NAME INTO NewsName';
'ALTER TABLE NEWS_TYPE_TBL ADD [NewsName] NUMBER(50) NULL';
// I want to add returning ticket name to here from 1st query.
END trigNews;
/

SQL Server after insert Trigger

CREATE TRIGGER [Ins] ON [databasename].[dbo].[tablename]
AFTER INSERT
AS
BEGIN
DECLARE #timerange as INT
SET #timerange = 40
UPDATE [databasename].[dbo].[tablename]
SET [column2] = (SELECT TOP 1 [column1]
FROM [CIMPLICITY].[dbo].[tablename] AS s2
WHERE DATEDIFF(MI,s2.[timestamp],[databasename].[dbo].[tablename].[timestamp]) <= ROUND([databasename].[dbo].[tablename].[column3],0))
WHERE
DATEDIFF(MI,[timestamp], SYSDATETIME()) < #timerange
END
GO
After adding a new row to the table, the trigger must recalculate the value in column 2.
The value in column 2 = the value in column 1 at offset backwards by minute equal to the value in column 3.
Trigger works, but the application that fill the table with data, stops working after the trigger.
I think that the error in the trigger code.
In advance I thank and sorry for my googletranslate english :)
Maybe your problem is that the trigger updates every record in the table, rather than just the inserted record(s).
If so, add another WHERE condition:
WHERE [primary key column] IN
(SELECT [primary key column] from inserted)
inserted is the alias for all records handled by the current INSERT statement.
Probably trigger recursion. Check the TRIGGER_NESTLEVEL and return if value is larger than one:
http://technet.microsoft.com/en-us/library/ms182737%28v=sql.90%29.aspx
I'm find a solution to the problem: it was necessary to add SET NOCOUNT ON after BEGIN.
I do not know why, but it working :)

Resources