Create a Trigger to insert a rows in another table - sql

After create a Stored Procedure in a Table " dbo.terms" to insert a data in it using this code:
CREATE PROCEDURE dbo.terms
#Term_en NVARCHAR(50) = NULL ,
#Createdate DATETIME = NULL ,
#Writer NVARCHAR(50) = NULL ,
#Term_Subdomain NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.terms
(
Term_en ,
Createdate ,
Writer ,
Term_Subdomain
)
VALUES
(
#Term_en = 'Cat' ,
#Createdate = '2013-12-12' ,
#Writer = 'Fadi' ,
#Term_Subdomain = 'English'
)
END
GO
I want to Create a Trigger in it to add another rows in a table dbo.term_prop
I used this code :
CREATE TRIGGER triggerdata
AFTER INSERT
ON dbo.terms
FOR EACH ROW
BEGIN
INSERT INTO dbo.term_prop VALUES
('قطة', term_ar, upper(:new.term_ar) , null , 'chat', term_fr, upper(:new.term_fr) , null ,'Animal', Def_en, upper(:new.Def_en) , null ,'حيوان', Def_ar, upper(:new.Def_ar) , null ,'Animal', Def_fr, upper(:new.Def_fr) , null);
END;
and it shows me an Error

To add more rows you can use SELECTED table.
This is a special table populated with rows inserted in your transaction.
An example is:
INSERT INTO dbo.term_prop VALUES
SELECT * FROM inserted
So you mustn't use FOR EACH ROW.
The correct definition of your trigger will be
CREATE TRIGGER triggername ON table AFTER INSERT
AS BEGIN
END

Joe answer is a good one and this is more a advice.
Avoid triggers, they can cause maintenance nightmares: are trick to maintain and debug.
If you want to inserts tables in another table after inserting in the first one just put that code in the same SP.
If you need a auto identity generated value you can do it by using ##identity or scope_identity() or ident_current().
Try to keep things simple.

Wow, I am still surprised that triggers are given a bad wrap! I wrote a dozen articles on them a long time ago ...
Like anything in life, the use of triggers depends on the situation.
1 - Trigger are great to track DDL changes. Who changed that table?
http://craftydba.com/?p=2015
2 - Triggers can track DML changes (insert, update, delete). However, on large tables with large transaction numbers, they can slow down processing.
http://craftydba.com/?p=2060
However, with today's hardware, what is slow for me might not be slow for you.
3 - Triggers are great at tracking logins and/or server changes.
http://craftydba.com/?p=1909
So, lets get back to center and talk about your situation.
Why are you trying to make a duplicate entry on just an insert action?
Other options right out of the SQL Server Engine to solve this problem, are:
1 - Move data from table 1 to table 2 via a custom job. "Insert Into table 1 select * from table 2 where etl_flag = 0;". Of course make it transactional and update the flag after the insert is complete. I am just considering inserts w/o deletes or updates.
2 - If you want to track just changes, check out the change data capture. It reads from the transaction log. It is not as instant as a trigger, ie - does not fire for every record. Just runs as a SQL Agent Job in the background to load cdc.tables.
3 - Replicate the data from one server1.database1.table1 to server2.database2.table2.
ETC ...
I hope my post reminds everyone that the situation determines the solution.
Triggers are good in certain situations, otherwise, they would have been removed from the product a long time ago.
And if the situation changes, then the solution might have to change ...

Related

Using BizTalk 2013r2 to UPSERT via WCF-SQL stored procedure

I'm currently trying to write a canonical schema to multiple related tables within a SQL DB, but I'm experience DUPLICATE KEY ID conflicts when it's evaluating whether the record exists prior to UPDATING/INSERTING.
BizTalk receives change records from the student management system every 5 minutes, maps them to a stored procedure and then calls that procedure which writes the changes to 5 tables in our master database.
I believe this is because I'm using an incorrect design pattern in the stored procedure.
Current Design:
IF EXISTS (Select student_id FROM student_modules WHERE student_id #student_id and module_id = #module_id)
-- THEN UPDATE THE RECORD
ELSE
-- INSERT THE RECORD
Logically this makes sense, but as BizTalk receives 2 change records with the exact same student and module ID at the same time, and then attempts to call the stored procedure for each record.
SQL then panics, because whilst it's evaluating the logic in the first message, it tries to execute the INSERT whilst evaluating the same logic in the second message - and tells me I'm trying to insert a DUPLICATE KEY.
I've tried using an UPSERT pattern that i found at the below link (design below), but that seems to lock the student_modules table completely.
BEGIN TRANSACTION;
UPDATE dbo.t WITH (UPDLOCK, SERIALIZABLE) SET val = #val WHERE [key] = #key;
IF ##ROWCOUNT = 0
BEGIN
INSERT dbo.t([key], val) VALUES(#key, #val);
END
COMMIT TRANSACTION;
https://sqlperformance.com/2020/09/locking/upsert-anti-pattern
Is there a cleaner approach to this that I'm missing?
You could use the MERGE Transact-SQL command
INSERT tbl_A (col, col2)
SELECT col, col2
FROM tbl_B
WHERE NOT EXISTS (SELECT col FROM tbl_A A2 WHERE A2.col = tbl_B.col);
You will also want to consider either changing your Orchestration so that it subscribes to further updates for the same student ID (a singleton type pattern) or to set your send port to ordered delivery, to prevent trying to update the same record at the same time.

Updates on a table must be inserted into another table

Is there a way to insert records into TABLE B if there is an update in TABLE A?
I don't want to use triggers.
The answer is we can use the OUTPUT clause of instead of triggers:
USE AdventureWorks2012;
GO
IF OBJECT_ID('dbo.vw_ScrapReason','V') IS NOT NULL
DROP VIEW dbo.vw_ScrapReason;
GO
CREATE VIEW dbo.vw_ScrapReason
AS (SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason);
GO
CREATE TRIGGER dbo.io_ScrapReason
ON dbo.vw_ScrapReason
INSTEAD OF INSERT
AS
BEGIN
--ScrapReasonID is not specified in the list of columns to be inserted
--because it is an IDENTITY column.
INSERT INTO Production.ScrapReason (Name, ModifiedDate)
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name,
INSERTED.ModifiedDate
SELECT Name, getdate()
FROM inserted;
END
GO
INSERT vw_ScrapReason (ScrapReasonID, Name, ModifiedDate)
VALUES (99, N'My scrap reason','20030404');
GO
The mechanism for doing this is called triggers.
Saying that you want to do this but don't want to use triggers is like saying you want to see the Eiffel Tower, but you don't want to go to France.
You could, I suppose, write a stored procedure that does all the logic that would have been in the trigger, if you can ensure that all data updates will be via that stored procedure.
If you don't want to use triggers, then you would have three options.
The first would be to wrap all inserts/updates/deletes in stored procedures. Then use only these stored procedures for data modification. This is actually the approach that I generally take.
Another would be to have a process that runs periodically looking for changes to the data. This is actually hard to do for updates. It is pretty easy to do for inserts because you can add an column with a default creation date, so you can readily find what has recently been added.
The third way is to use SQL Server Change Tracking (see here).
You could make a stored procedure that performs both the update in table A and the insert in table B
CREATE PROCEDURE proc_name
#id
#param1
AS
BEGIN
update tableA
set field1 = #param1
where ID = #id
insert into tableB(field1)
values(#param1)
END

INSERTED magic table in case of in place Update

Today I saw a nice post in which the author has proven that if you are updating a column of a table without any indexes defined, an "in place update" will occur and not traditional delete\insert.
Considering this I run a small test where I have created an Update trigger on table and try to access the INSERTED magic table and here is the catch.
I am able to access the INSERTED magic table, can someone explain me if in place update is not using traditional Delete\Insert? How come one can access the magic tables?
These are my SQL statement to prove this thing.
Main table :
CREATE TABLE TestingUpdate1 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate1 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
CHECKPOINT -- truncate the log, DB is in simple recovery.
UPDATE TestingUpdate1
SET SomeString = 'NotFour'
WHERE ID = 4 -- one row
SELECT Operation, Context, AllocUnitName, [Transaction Name], Description FROM fn_dblog(NULL, NULL) AS TranLog
Second table :
CREATE TABLE TestingUpdate4 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate4 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
Trigger :
CREATE TRIGGER ViewCustomerTrigger ON TestingUpdate1
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE TestingUpdate4
SET SomeString = i.SomeString
FROM INSERTED i
END
GO
select * from TestingUpdate4
select * from TestingUpdate1
Thanks in advance
The INSERTED and DELETED tables in an update trigger always show the logical before and after versions of the updated rows.
Whether that is physically implemented as an in place update or as an insert/delete is execution plan dependant and irrelevant to the contents of these tables.

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.

Stored Procedures and Triggers in data base

what do Stored Procedures and Triggers in data base mean ?
how can i create Stored Procedures ?
how can i crest Triggers ?
if you have simple examples for each of these .please help :)
what i know is only about trigger which is activated if an action of(insert or delete or update ) violates the constrains specified but i don't know how to create ,so again if any have example please
Think of a Stored Procedure as a method in your code. It runs a specific set of instructions.
Stored Procedures are created to, for example, manage complex sets of data that would normally be a pain to handle along in your code.
You can create a Stored Procedure with the following instructions:
Oracle
CREATE OR REPLACE PROCEDURE P_PROCEDURE_NAME (
pParameter1 NUMBER
, pParameter2 VARCHAR2(100 Bytes)
) AS
BEGIN
-- Procedure code here...
END;
SQL Server
CREATE PROCEDURE cspProcedureName
#parameter1 int
, #parameter2 nvarchar(100)
AS
-- Procedure code here...
Oracle
As for the Triggers, they are sets of code called upon an action occuring to the related table. For instance, in Oracle, there are no INDENTITY columns such as SQL Server offers. Instead, Sequences are used along with Triggers to simulate the same. Hence, you will need to create an Oracle SEQUENCE, then the TRIGGER to update the ID field of your table.
CREATE SEQUENCE SEQ_CUSTOMERS
MINVALUE 1
MAXVALUE 65535
START WITH 1
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER TRG_CUSTOMERS_INSERT
BEFORE INSERT
ON TBL_CUSTOMERS
FOR EACH ROW
BEGIN
:NEW.CUST_ID := SEQ_CUSTOMERS.NEXTVAL;
END;
SQL Server
A trigger example in SQL Server would be updating automatically the update datetime of a record. Consider the following:
CREATE TABLE Customers (
CustId int NOT NULL IDENTITY(1, 1) PRIMARY KEY
, CustName nvarchar(100) NOT NULL
, CreatedOn datetime DEFAULT GETDATE()
, LastUpdate datetime NOT NULL
)
GO
CREATE TRIGGER trgCustomersUpdt
AFTER UPDATE
ON Customers
AS
update Customers
set LastUpdate = GETDATE()
where CustId = inserted.Custid
GO
DISCLAIMER
This code has not been tested and may require minor changes for it to work properly against its respective RDBMS.
To sum it up, Triggers are mainly used to as illustrated here, despite there are many other possible use, such as building up an history of table changes that occured throught time, keeping all records of transactions into an history table or the like. The Stored Procedures are mainly used to perform complex database tasks where this would get too complex to do in code.