Alternative to row level triggers? - sql

MS SQL Server doesn't have row level triggers, correct? If I needed to insert a row from within a trigger and then insert another row, based on the result of the first insert, would a cursor be the best solution?
For example, is there a better way to do this:
CREATE TABLE t1 (foo int)
CREATE TABLE t2 (id int IDENTITY, foo int)
CREATE TABLE t3 (t2_id int)
GO
CREATE TRIGGER t1_insert_trg ON t1 FOR INSERT AS
DECLARE c CURSOR FOR
SELECT foo FROM inserted
DECLARE #foo int
OPEN c
FETCH NEXT FROM c INTO #foo
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO t2 (foo) VALUES (#foo)
INSERT INTO t3 (t2_id) VALUES (##IDENTITY)
FETCH NEXT FROM c INTO #foo
END
CLOSE c
DEALLOCATE c

I assume you are on 2005 or better? If so, look into the OUTPUT clause, you shouldn't need row-level triggers. For example:
USE tempdb;
GO
CREATE TABLE t1 (foo int);
CREATE TABLE t2 (id int IDENTITY, foo int);
CREATE TABLE t3 (t2_id int);
GO
CREATE TRIGGER t1_insert ON t1
FOR INSERT AS
BEGIN
DECLARE #new_rows TABLE(new_id INT, old_foo INT);
INSERT t2(foo)
OUTPUT inserted.id, inserted.foo
INTO #new_rows
SELECT foo
FROM inserted;
INSERT t3 SELECT new_id FROM #new_rows;
END
GO
INSERT t1(foo) SELECT 1 UNION ALL SELECT 5;
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
GO
DROP TABLE t1,t2,t3;
You could also manage this by having a trigger on T1 that inserts into T2, then a trigger on T2 that inserts into T3. This isn't going to be as efficient IMHO, and is not easier to manage, but I will submit that it is easier to follow (and may be your only option if you are stuck on 2000). Both could be set-based and wouldn't need cursors or any other row-by-row processing method.
USE tempdb;
GO
CREATE TABLE t1 (foo int);
CREATE TABLE t2 (id int IDENTITY, foo int);
CREATE TABLE t3 (t2_id int);
GO
CREATE TRIGGER t1_insert ON t1
FOR INSERT AS
BEGIN
INSERT t2(foo)
SELECT foo FROM inserted;
END
GO
CREATE TRIGGER t2_insert ON t2
FOR INSERT AS
BEGIN
INSERT t3(t2_id)
SELECT id FROM inserted;
END
GO
INSERT t1(foo) SELECT 1 UNION ALL SELECT 5;
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
GO
DROP TABLE t1,t2,t3;
(BTW, if you are going for identity values, use SCOPE_IDENTITY(), not ##IDENTITY.)

You might be able to avoid a cursor or the need to know what identity was inserted using the following inserts.
Insert INTO t2 (foo) Select foo from inserted
Insert into t3 (t2_id) Select t2.id from t2
inner join inserted i on t2.foo = i.foo

Why not cascade the triggers - Use an INSERT trigger on T2 to perform the insert on T3. Then you can avoid the cursor within t1_insert_trg and just use inserted - as in:
CREATE TRIGGER t1_insert_trg ON t1 FOR INSERT AS
INSERT INTO t2
SELECT foo FROM inserted -- fires t2 INSERTED trigger
CREATE TRIGGER t2_insert_trg ON t2 FOR INSERT AS
INSERT INTO t3
SELECT id FROM inserted

Related

Insert or update multiples rows

I have two tables where TableA has latest data and TableB has some old data. I want to update TableB's data if it matches id with TableA and if doesn't match insert new row in TableB.
I got a solution from stackOverflow
begin tran
if exists (select * from t with (updlock,serializable) where pk = #id)
begin
update t set hitCount = hitCount+1
where pk = #id
end
else
begin
insert t (pk, hitCount)
values (#id, 1)
end
commit tran
But it seems I need to pass #id each time, may be I am not getting it in the correct way. I have hundreds of row to update/insert from tableA.
Think relationally.
SQL Server always operates sets. A single row is just a set of 1 row.
Here is a simple example of two step update - insert operations
create table #tableA(id int, [year] int, updated_value int)
insert #tableA(id,[year],updated_value)
values
(1,1990,85),
(2,1991,70),
(3,1992,80)
create table #tableB(id int, [year] int, score int)
insert #tableB(id,[year],score)
values
(1,1990,50),
(4,1995,20)
update #tableA set
updated_value=b.score
from #tableA a
inner join #tableB b on a.id=b.id --inner is important
insert #tableA(id,[year],updated_value)
select b.id,b.[year],b.score
from #tableB b
left join #tableA a on a.id=b.id --left is important
where a.id is null -- and this line too
select * from #tableA
If you wish you can combine update and insert in a single merge operation.
merge #tableA as tgt
using #tableB as src
on src.id=tgt.id
when matched then
update set updated_value=src.score
when not matched then
insert(id,[year],updated_value)
values(id,[year],score)
; -- semicoloumn is required
select * from #tableA

Joining multiple table Sql trigger

Hi I am newbie to SQL trigger. since I tried and searched on online and I dont find any clear outcome.
so here is my problem.
I have three tables:
TABLE1 :
ID NAME (columns )
1 prabhu
TABLE2 :
Id COUNTRY (columns )
1 India
I want this to send to log table if anything like insert/update happen in table2
The SQL(DB2) trigger has to do the following and the result should be in log table like this
LOGTABLE:
ID NAME COUNTRY
1 prabhu India
Your help really appreciated.
Try this,
-- Create tables
create table table1(id int, empName varchar(20));
create table table2(id int, country varchar(20));
create table logtable(id int, empName varchar(20), country varchar(20));
-- Create trigger
CREATE TRIGGER logtableAfterInsert ON table2
after INSERT,DELETE,UPDATE
AS
BEGIN
declare #empid int;
declare #empname2 varchar(20);
declare #empcountry varchar(20);
select #empid=i.id from inserted i;
select #empcountry=i.country from inserted i;
select #empname2=tbl1.empName from table1 tbl1 where tbl1.id=#empid;
insert into logtable values(#empid,#empname2,#empcountry);
PRINT 'Inserted'
END
GO
After that insert the values,
insert into table1 values(1, 'prabhu');
insert into table2 values (1, 'India');
Check the results,
select * from table1;
select * from table2;
select * from logtable;
Hope this resolves...
BTW, You need to add the foreign key constraint.
CREATE OR REPLACE TRIGGER logtableAfterUpdate
AFTER UPDATE ON table2
REFERENCING NEW AS NAUDIT OLD AS OAUDIT
FOR EACH ROW MODE DB2SQL
--BEGIN --ATOMIC
insert into logtable
values(
(select id from table2 tbl2 where tbl2.id =OAUDIT.id),
(select empName from table1 tbl1 where tbl1.id=(select id from table2 tbl2 where tbl2.id =OAUDIT.id)),
(select country from table2 tbl2 where tbl2.id =OAUDIT.id)
);
--END;

Insert into 2 tables from a single select query using TSQL

I am trying to insert into 3 tables from one single select statement. Here is what I am trying to do:
insert into dbo.temp1 (name, location, city)
select name, location, city from mytable.
I want to be able to insert into 3 tables once I run the select statement like inserting into temp1, temp2 and temp3.
How can I do this? Thanks.
You can do it maximum for 2 tables with using output:
insert into dbo.temp1 (name, location, city)
output inserted.name, inserted.location, inserted.city into temp2
select name, location, city from mytable
You can't do this in one step*
What you can do is to insert the initial query into a #temp table (or a #table variable) as a staging area, and then insert into the tables from there. Wrap the steps in a transaction to retain ACID:
BEGIN TRAN
select name, location, city
into #TEMP
from mytable;
insert into temp1(name, location, city)
select name, location, city
from #TEMP;
-- Same for temp2 and temp3.
COMMIT TRAN
* Excluding hacks such as a view with an Instead-of Trigger.
The staging table is important from a concurrency point of view, as repeating the original query 3 times may result in different results if there are interim concurrent changes to the source table.
You can.
With a trick.
Create a view, then create an 'instead of' trigger for insert on that view where you insert the stuff into your tables. If you now insert into your view, you finally insert data in 3 tables. Here's a demo
-- 1. create 3 test tables
create table t1( id int, f1 varchar(20))
create table t2( id int, f2 varchar(20))
create table t3( id int, f3 varchar(20))
go
-- 2. create the view
create view Tt as
select t1.ID, t1.f1, t2.f2,t3.f3
from t1
join t2 on t1.ID=t2.ID
join t3 on t1.ID=t3.id
go
-- 3. create the trigger
create trigger Tr_Test on Tt INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
insert into t1 select id,f1 from inserted
insert into t2 select id,f2 from inserted
insert into t3 select id,f3 from inserted
END
GO
-- 4. now do your insert with a single select
insert into tt
select 1,'A','B','C'
-- 5. and watch the 3 tables
select * from t1
select * from t2
select * from t3
voilá, one insert, 3 tables got modified. Wwe don't count the hidden trigger, do we ;-)
There is no way to insert into X tables with one query (Ok it its with insert and output to table).
So you have to write 3 queries.
Or you can generate SQL statments with dynamic queries.
I don't believe you can insert into multiple tables in one statement. You can definitely do it in one transaction, however.
BEGIN TRANSACTION
INSERT INTO dbo.temp1 (name, location, city)
SELECT name, location, city
FROM myTable
INSERT INTO dbo.temp2 (name, location, city)
SELECT name, location, city
FROM myTable2
COMMIT TRANSACTION
You can insert into multiple tables with one select statement using a TRIGGER.
CREATE TRIGGER TEMP2_TEMP3_INSERT ON TEMP1
AFTER INSERT AS
BEGIN
/* create your insert statements for TEMP2 and TEMP3 here
referencing the data from the first insert */
END;
GO
MySQL doesn't support multi-table insertion in a single INSERT statement. Oracle is the only one I'm aware of that does, oddly...
However, you CAN use a transaction and have both of them be contained within one transaction.
MySQL:
START TRANSACTION;
INSERT INTO table1 VALUES ('1','2','3');
INSERT INTO table2 VALUES ('1','2','3');
COMMIT;
SQL Server:
BEGIN TRAN;
INSERT INTO table1 VALUES ('1','2','3');
INSERT INTO table2 VALUES ('1','2','3');
COMMIT;
SQL Server with error catching/rollback:
BEGIN TRANSACTION [Tran1]
BEGIN TRY
INSERT INTO table1 VALUES ('1','2','3')
INSERT INTO table2 VALUES ('1','2','3')
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
GO

How to create this kind of SQL trigger?

Let's say in sqlite3 I create a table like this.
CREATE TABLE table1 (int a);
I want to create a trigger that activates before an insertion into table1. If the number inserted into table1 is greater than 10, then I want the trigger to not allow the insertion. What's the code to do that?
CREATE TRIGGER [dbo].[table1_trigger]
ON [dbo].[table1]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #a INT
SET #a = (SELECT a FROM Inserted)
IF #a < 10 BEGIN
INSERT INTO dbo.table1 (a) VALUES (#a)
END
END

please help me to create multi insert query

I have got two table
create table t1(cid int, isnews int)
create table t2(nid int,cid int, isnews int)
situations is like this:
if t2 contain t2.cid = t1.cid then the t2.isnews = t1.news and
if t2 not contain cid of t1 then new record should be inserted in t2 and that t1.cid, t1.isnews should be inserted in t2..
and complete table should be done in single query... i have done the updation part but not able to do insertion part..
update query:
UPDATE t22
SET t22.isnews = t11.isnews
FROM t2 AS t22
JOIN t1 AS t11
ON t11.cid= t22.cid
i have prepared below cursor for insert... is it good? :
DECLARE #clntid INT
DECLARE #clntnewsltr INT
DECLARE clientnews CURSOR FOR
SELECT clientid,newsLetter
FROM clients
WHERE clientid NOT IN (SELECT clientid FROM clientprivacy)
OPEN clientnews
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO clientprivacy (clientId,tdNewsLetters) VALUES(#clntid, #clntnewsltr)
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
END
CLOSE clientnews
DEALLOCATE clientnews
I think this is the kind of thing you're after:
--INSERT t2 (cid, isnews)
SELECT t1.cid, t1.isnews
FROM t1
LEFT JOIN t2 ON t1.cid = t2.cid
WHERE t2.cid IS NULL
I've commented out the INSERT line - I recommend you run the SELECT on it's own first to check it does give you the correct result (all records from t1 that don't have a matching cid in t2).
I've assumed t2.nid is an IDENTITY column.
You will be so much better off without cursors :) Cursors take MUCH longer to run in large data sets.
It is true you can use a LEFT JOIN, but you can also use a SELECT in your WHERE clause. Most of the time it's a style choice.
CREATE TABLE table1(col_1 int, col_2 int)
CREATE TABLE table2(nid int, col_1 int, col_2 int)
INSERT INTO table2 (col_1,col_2)
SELECT col_1,col_2
FROM table1
WHERE col_1 NOT IN (SELECT col_1 FROM table2)