Null values INSERTED in trigger - sql-server

I want to copy content of one table to another table in the same database.
For this I wrote trigger on source table which triggered on AFTER INSERT UPDATE, there are 2 uniqueidentifier fields in the table which generates values based on newid() as default binding. Based on this uniqueidentifier I am checking whether the record is present on the destination table or not if present then it will update and if not present then insert dataset into the table.
Problem is when i insert a new record the INSERTED in trigger give me NULL values for the uniqueidentifier fields.
In may case only one row is either update or insert so cursor is not used.
Below is my code, I am getting null values in #OriginalTable_MoveDataUID and #OriginalTable_ProcedureUID. Both the MoveDataUID and ProcedureUID are uniqueidentifier fileds.
Please share your thoughts or any alternative for this.
ALTER TRIGGER [dbo].[spec_ref_movedata_procedures_ToUpdate]
ON [dbo].[spec_ref_movedata_procedures]
AFTER INSERT, UPDATE
AS
BEGIN
SET XACT_ABORT ON
BEGIN DISTRIBUTED TRANSACTION
DECLARE #OriginalTable_MoveDataUID NVarchar (100)
DECLARE #OriginalTable_ProcedureUID NVarchar (100)
DECLARE #PresentInHistoryYesNo int
SELECT #OriginalTable_MoveDataUID= MoveDataUID,#OriginalTable_ProcedureUID=ProcedureUID FROM INSERTED
-- inserted for checking purpose
INSERT INTO ERP_Test_NK_spec_ref_movedata_procedures_history_2 (MovedataUID,ProcedureUID) VALUES
(#OriginalTable_MoveDataUID,#OriginalTable_ProcedureUID)
SELECT #PresentInHistoryYesNo = count(*) from spec_ref_movedata_procedures_history WHERE MoveDataUID=#OriginalTable_MoveDataUID AND ProcedureUID=#OriginalTable_ProcedureUID
IF #PresentInHistoryYesNo = 0
BEGIN
-- insert opertions
print 'insert record'
END
ELSE IF #PresentInHistoryYesNo = 1
BEGIN
-- update opertions
print 'update record'
END
COMMIT TRANSACTION
SET XACT_ABORT OFF
END

Instead of using variables, you could do this:
INSERT INTO ERP_Test_NK_spec_ref_movedata_procedures_history_2 (MovedataUID,ProcedureUID)
SELECT MoveDataUID,ProcedureUID FROM INSERTED

Related

How to relate INSERTED and DELETED columns in INSTEAD OF INSERT, UPDATE trigger?

We have an INSTEAD OF INSERT, UPDATE trigger on a view migrating it from Oracle. It inserts or updates data in underlying table of the view instead of the view. Furthermore, for any insert on the view it updates rather than insert if an id in the table matches with the insert id. So, if multiple entries are thrown at table some will have new entries while matching ids will have an update. The trigger also uses an cursor on an inserted tables to mimic Oracle's FOR EACH ROW.
Now the problem, we want to update some values in the inserting/updating rows based on new values (inserting/updating), old values (deleting) or assign a value if both of these values do not satisfy. Underlying table does not have any PK. How can we join INSERTED and DELETED tables so that we can refer NEW and OLD values for each column in each updating row ?
CREATE TRIGGER INSTEAD_OF_TRG ON VIEW V
INSTEAD OF INSERT, UPDATE AS
BEGIN
DECLARE INSERTED_CUR CURSOR LOCAL FOR SELECT ID, NAME, VALUE, MODIFIEDDATE
DECLARE #ID VARCHAR(6), NAME VARCHAR(50), VALUE VARCHAR(10), MODIFIEDDATE DATETIME2
FETCH NEXT FROM INSERTED_CUR INTO #ID, #NAME, #VALUE, #MODIFIEDDATE
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #LVALUE VARCHAR(10)
-- HERE WE WANT TO CHECK FOR NEW AND UPDATED VALUES OF COLUMN VALUE
IF #VALUE IS NOT NULL --- VALUE FROM INSERTED IS NOT NULL
SET #LVALUE = #VALUE
ELSE
**IF #OLDVALUE IS NOT NULL
SET #LVALUE = #OLDVALUE**
-- HOW CAN WE REFER OLD VALUE HERE IN CASE OF UPDATES. IN ORACLE THEY ARE DOING IT LIKE :NEW.VALUE IN FIRST IF AND :OLD.VALUE IN ELSE IF FOR EACH ROW.
SET #LVALUE = #OLDVALUE
ELSE
SET #LVALUE = 'DEFAULT'
-- UPDATE AND INSERT CODE CONTINUES WHERE WE USE #LVALUE--

ROLLBACK make data in INSERTED table is removed in AFTER INSERT TRIGGER

I give an example to show my problem. I created a table like this:
CREATE TABLE a
(
id INT
)
I then created an AFTER INSERT trigger to not allow insert id = 1 into table a:
CREATE TRIGGER [dbo].[insert_a]
ON [dbo].[a] AFTER INSERT
AS
BEGIN
DECLARE #id INT
SELECT #id = id FROM inserted
IF #id = 1
BEGIN
RAISERROR('1', 12, 1)
ROLLBACK;
END
SELECT * FROM inserted
END
Then I insert id = 1 into table a:
INSERT INTO a VALUES(1)
I get nothing from INSERTED table.
I realize that when I ROLLBACK then + the data in table a was rolled back (I know) and data in INSERTED table is also removed. Why is that?
If I change the AFTER INSERT trigger to an INSTEAD OF INSERT trigger:
ALTER TRIGGER [dbo].[insert_a]
ON [dbo].[a] INSTEAD OF INSERT
AS
BEGIN
DECLARE #id INT
SELECT #id = id FROM inserted
IF #id = 1
BEGIN
RAISERROR('1', 12, 1)
ROLLBACK
END
SELECT * FROM inserted
END
INSERT INTO a VALUES(1)
Then I get the result:
id
1
That means data in INSERTED table is not removed though have been ROLLBACK.
Help me explain deeply what happens inside trigger?
This is the intended behaviour as far as I know. It's just that AFTER may be a bit misleading depending on how you look at it.
"The trigger and the statement that fires it are treated as a single transaction, which can be rolled back from within the trigger. If a severe error is detected, the entire transaction automatically rolls back.".
https://msdn.microsoft.com/en-us/library/ms178110.aspx

Insert values in second table using triggers

I have one table called [FridgeTemperture], when any record inserted it should add one value in the new table MpSensors. But records are not being inserted in the new table when a record is inserted.
Error
Explicit value must be specified for identity column in table
'MpSensors' either identity_insert is set to ON or when a replication
user is inserting into a not for replication identity column.
CREATE TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
SET IDENTITY_INSERT MpSensors ON;
SET NOCOUNT ON;
DECLARE #fridge_temp varchar(10)
INSERT INTO MpSensors(fridge_temp)
VALUES(#fridge_temp)
SET IDENTITY_INSERT MpSensors OFF;
END
GO
table schema
CREATE TABLE [dbo].[MpSensors](
[id] [int] IDENTITY(1,1) NOT NULL,
[fridge_temp] [varchar](10) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[FridgeTemperture](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ShopId] [nvarchar](4) NULL,
[Fridgetemp] [decimal](4, 2) NOT NULL,
[UpdatedDate] [datetime2](7) NOT NULL
GO
You don't need the set identity_insert on if you are not attempting to insert values to the identity column. Also, your current insert statement, if you loose the set identity_insert, will simply inside a single null row for any insert statement completed successfully on the FridgeTemperture table.
When using triggers, you have access to the records effected by the statement that fired the trigger via the auto-generated tables called inserted and deleted.
I think you are after something like this:
CREATE TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
INSERT INTO MpSensors(fridge_temp)
SELECT CAST(Fridgetemp as varchar(10))
FROM inserted
END
Though I can't really see any benefit of storing the same value in two different places, and in two different data types.
Update
Following our conversation in the comments, you can simply use an update statement in the trigger instead of an insert statement:
UPDATE MpSensors
SET fridge_temp = (
SELECT TOP 1 CAST(Fridgetemp as varchar(10))
FROM inserted
ORDER BY Id DESC
)
This should give you the latest record in case you have an insert statement that inserts more than a single record into the FridgeTemperture table in a single statement.
create TRIGGER [dbo].[FridgeTemperature_INSERT]
ON [dbo].[FridgeTemperture]
AFTER INSERT
AS
BEGIN
UPDATE MpSensors
SET fridge_temp = CAST(Fridgetemp as varchar(10))
FROM inserted
END
You need to use Select statement with CAST as [fridge_temp] is varchar in MpSensors table in Trigger. Try like this:
CREATE trigger <table_name>
ON <table_name>
AFTER Insert
AS
BEGIN
INSERT INTO <table_name>(column_name)
Select CAST(column_name as varchar(10))
FROM inserted
END
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. The rows in the inserted table are copies of the new rows in the trigger table.

Continue inserting data in tables skipping duplicate data issue

set xact_abort off;
begin tran
DECLARE #error int
declare #SQL nvarchar(max)
set #SQL=N'';
select #SQL=some select query to fetch insert scripts
begin try
exec sp_executesql #SQL
commit
end try
begin catch
select #error=##Error
if #error=2627
begin
continue inserting data
end
if #error<>2627
begin
rollback
end
end catch
I am unable to continue inserting data when any duplicate data comes. Is there any alternative way to continue running SQL queries irrespective of duplicate data? I don not want to alter the index or table.
I am unable to continue inserting data when any duplicate data comes. Is there any alternative way to continue running sql queries irrespective of duplicate data. I dont want to alter the index or table.
What you can do is change the insert scripts as you call them, in this pseudo statement:
select #SQL=some select query to fetch insert scripts
Change the generation script: instead of generating INSERT INTO ... VALUES(...) statements, generate IF NOT EXISTS(...) INSERT INTO ... VALUES(...) statements
These insert statements should first check if a key already exists in the table. If your insert statements are of the form
INSERT INTO some_table(keycol1,...,keycolN,datacol1,...,datacolM)VALUES(keyval1,...,keyvalN,dataval1,...,datavalM);
You can rewrite those as:
IF NOT EXISTS(SELECT 1 FROM some_table WHERE keycol1=keyval1 AND ... AND keycolN=keyvalN)
INSERT INTO some_table(keycol1,...,keycolN,datacol1,...,datacolM)VALUES(keyval1,...,keyvalN,dataval1,...,datavalM);
Change the generation script: instead of generating INSERT INTO ... SELECT ..., generate INSERT INTO ... SELECT ... WHERE NOT EXISTS(...) statements
You can change these statements to only insert if the key does not exist in the table yet. Suppose your insert statements are of the form:
INSERT INTO some_table(keycol1,...,keycolN,datacol1,...,datacolN)
SELECT _keycol1,...,_keycolN,datacol1,...,datacolN
FROM <from_clause>;
You can rewrite those as:
INSERT INTO some_table(keycol1,...,keycolN,datacol1,...,datacolN)
SELECT _keycol1,...,_keycolN,datacol1,...,datacolN
FROM <from_clause>
WHERE NOT EXISTS(SELECT 1 FROM some_table WHERE keycol1=_keycol1 AND ... AND keycolN=_keycolN);
Replace the target table name in #SQL with a temporary table (a so-called staging table), then insert from the temporary table to the target table using WHERE NOT EXISTS(...)
This way you would not have to change the insert generation script. First create a temporary table that has the exact same structure as the target table (not including the primary key). Then replace all instances of the target table name in #SQL with the name of the temporary table. Run the #SQL and afterwards insert from the temporary table to the target table using a WHERE NOT EXISTS(...).
Suppose the target table is named some_table, with key columns key_col1,...,key_colN and data columns datacol1, ..., datacolM.
SELECT * INTO #staging_table FROM some_table WHERE 1=0; -- create staging table with same columns as some_table
SET #SQL=REPLACE(#SQL,'some_table','#staging_table');
EXEC sp_executesql #SQL;
INSERT INTO some_table(keycol1,...,keycolN,datacol1,...,datacolN)
SELECT st.keycol1,...,st.keycolN,st.datacol1,...,st.datacolN
FROM #staging_table AS st
WHERE NOT EXISTS(SELECT 1 FROM some_table WHERE keycol1=st.keycol1 AND ... AND keycolN=st.keycolN);
DROP TABLE #staging_table;

Syntax for SQL Trigger to Insert Data in another DB and also to update any field in another db adfter a field is edited

Here is the scenario - I will be specific. I have a "bridged" DB on Sql Called [Fulcrum_Xfer] I use this bridged because the Main Db called [Fulcrum UAT] is using a bigint datatype for some fields and thereby displaying in my Access front end "#Deleted data" in all fields - This behavior CANNOT be changed in the present design (the bigint has to stay) so I have the exact table name and fieldnames in my [Fulcrum_Xfer] DB - the OrderNO field in Orders table in [Fulcrum_Xfer] is int and there is no primary key
What I need to have done by tomorrow under threat of some "you let us down" is the following
the table that initially gets data inserted or updated into is called Orders and is in the [Fulcrum_Xfer] database that structure is as follows
OrderNo int Unchecked
OrderDate smalldatetime Unchecked
ApplicationTenantLinkId int Unchecked
OrderStatus int Unchecked
the table that receives the triggered data from Orders in FulCrum_Xfer is called Orders and it is in Database Fulcrum UAT
The structure is
OrderNo bigint Unchecked Primarykey
OrderDate smalldatetime Unchecked
ApplicationTenantLinkId Bigint Unchecked
OrderStatus int Unchecked
I need two trigger statements that will insert a new record into Orders in [Fulcrum UAT] after I insert it into Orders in [FulCrum_Xfer]
and
I need a trigger that will update any field in Orders in [Fulcrum UAT] when I make a change in Orders in [Fulcrum_Xfer]
I do not know where the trigger goes other than maybe Database Triggers in [FulCrum_XFer] but I get freaked out by the template syntax (do not think I need all that) and I do not know how to write the syntax for each task
I am a very experienced VB / VBA developer and have used ADO for building and calling stored procedures on SQL but have never had to do this type of task on the SQL Server - please do not treat me like a dunce - but this is very important in my job right now.
Well, of cousre I have no way to test it, but I think this is how you'd write the INSERT trigger:
USE [Fulcrum_Xfer]
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER dbo.trOrders_Insert
ON dbo.Orders
AFTER INSERT AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Just INSERT everything from the [inserted] pseudotable into
--the target table
INSERT INTO [Fulcrum UAT].dbo.Orders
(OrderNo, OrderDate, ApplicationTenantLinkId, OrderStatus)
SELECT OrderNo, OrderDate, ApplicationTenantLinkId, OrderStatus
FROM inserted;
END
GO
Copy and paste this into a Query window in Management Studio and execute it.
Here's how I'd do the UPDATE trigger. Again, untested ...
USE [Fulcrum_Xfer]
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER dbo.trOrders_Update
ON dbo.Orders
AFTER UPDATE AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Just UPDATE everything matching [inserted] pseudotable
--into the target table
--(NOTE: This assumes that UPDATES will never change the PK/OrderNo)
UPDATE [Fulcrum UAT].dbo.Orders
SET OrderDate = ins.OrderDate,
ApplicationTenantLinkId
= ins.ApplicationTenantLinkId,
OrderStatus = ins.OrderStatus
FROM [Fulcrum UAT].dbo.Orders As tar
JOIN inserted as ins ON tar.OrderNo = ins.OrderNo;
--(also, performance may not be great as the JOIN columns are
-- different datatypes)
END
GO
Given:
Database db1 with table t1 and fields (username1, password1)
Database db2 with table t2 and fields (username2, password2)
If you want insert or update t2 in db2 when changes (assume insert) occurred in db1, you should add trigger to t1 in db1:
CREATE TRIGGER `trigger_name` AFTER INSERT ON `t1`
FOR EACH ROW INSERT INTO `db2`.`t2`( `username2`, `password2`)
VALUES ( new.username1, new.password1)
(I am assuming both databases are hosted on the same server.)
Try This
USE BioStar;// Which data base you want to create trigger
GO
CREATE TRIGGER trgAfterInsertnew ON [dbo].[TB_EVENT_LOG]
FOR INSERT
AS
declare #nDateTime int;
declare #nReaderIdn int;
declare #nEventIdn int;
declare #nUserID int;
declare #nIsLog smallint;
declare #nTNAEvent smallint;
declare #nIsUseTA smallint;
declare #nType smallint;
select #nDateTime=i.nDateTime from inserted i;
select #nDateTime=i.nReaderIdn from inserted i;
select #nEventIdn=i.nEventIdn from inserted i;
select #nUserID=i.nUserID from inserted i;
select #nIsLog=i.nIsLog from inserted i;
select #nTNAEvent=i.nTNAEvent from inserted i;
select #nIsUseTA=i.nIsUseTA from inserted i;
select #nType=i.nType from inserted i;
insert into [HRM].dbo.Device_Data
(nDateTime,nReaderIdn,nEventIdn,nUserID,nIsLog,nTNAEvent,nIsUseTA,nType)
values(#nDateTime,#nDateTime,#nEventIdn,#nUserID,#nIsLog,#nTNAEvent,#nIsUseTA,#nType);
--set #audit_action='Inserted Record -- After Insert Trigger.';
PRINT 'AFTER DELETE TRIGGER fired.'
GO

Resources