MSSql Trigger on Update Insert (Updatecounter) - sql-server

I have a table named Table1 with two fields Systemname and Updatecount.
Each insert with Systemname "SAP" should set the Updatecount to 1 (initial value).
If the Field Systemname gets an Update with the defined Value "SAP", the Field Updatecount should be increased by 1.
How can i define the trigger ?

create trigger tr on Table1 for insert,update
as
begin
if update(Systemname)
update Table1
set UpdateCount = (case when not exists(select * from deleted) then 1 else UpdateCount + 1 end)
from Table1
inner join inserted on Table1.[<YourPKField>] = inserted.[<YourPKField>]
where inserted.Systemname = 'SAP'
end
GO

There is a good article on triggers here:
http://www.codeproject.com/Articles/38808/Overview-of-SQL-Server-database-Triggers
You need to create:
CREATE TRIGGER [TRIGGER_ALTER_COUNT] ON [dbo].[tblTriggerExample]
FOR INSERT, UPDATE
AS
BEGIN
DECLARE #Var INT
SELECT #Var = COUNT(*) FROM INSERTED
UPDATE [dbo].[tblTriggerExample] SET AlterCount = AlterCount + Var
,LastUpdate = GETDATE()
WHERE TransactionID = #TransID
SELECT #Var = COUNT(*) FROM UPDATED WHERE SystemNAme = 'Var'
UPDATE [dbo].[tblTriggerExample] SET AlterCount = AlterCount + #Var
,LastUpdate = GETDATE()
WHERE TransactionID = #TransID
END

Related

Converting SQL Server script to Oracle

I have a script that I created that works fine in SQL Server, but now I need to make it work in Oracle and I am having issues getting the script converted.
Here is my SQL Server Script:
-- run the commented out statement in another query analyzer window to stop the script
-- update ##stopsign set val = 1
set nocount on
--declare variables
declare #morework int
declare #archivecount int
declare #stopsign int
--if working tables exists clear them, if not create and initialize them
if (object_id('tempdb..##stopsign') is null)
create table ##stopsign (val int)
else
delete from ##stopsign
insert into ##stopsign values (0)
if (object_id('tempdb..#tempdins') is null)
create table #tempdins (tempdin varchar(255) not null, processed int null)
else
delete from #tempdins
--initialize #tempdins working table with all the records eligible to be unarchived
--edit the select statement if needed to change the records to be unarchived
insert into #tempdins(tempdin)
select tempdin
from document
where archivestatus = 'C'
and status = 'U'
option (MAXDOP 1)
--inialize variables with current values
select #archivecount = (select count(*) from unarchs)
select #stopsign = (select val from ##stopsign)
select #morework = 1
--while there is more to do, unarchs table has less then 1000 records, and the stopsign value is 0 loop
while (#morework >= 1 and #stopsign = 0)
begin
if #archivecount <1000
begin
-- number to be processed at once
-- change this value if you would like to dump more in to the unarchs table at once
set rowcount 100
update #tempdins
set processed = 0
where processed is null
--reset rowcount
set rowcount 0
--populate the unarchs table with valid values
--this will unarchive at the page (lowest) level
insert into unarchs (drawer,foldernumber,packageid,docid,pagenumber,unarchtype,unarchdate,unarchtime,userid,unarchdays)
select distinct drawer,foldernumber,packageid,docid,pagenumber,'Page','20061128','12:00:00','ADMIN',360
from document
where tempdin in (select tempdin
from #tempdins
where processed = 0)
--update with rowcount to see if finished
select #morework = ##rowcount
--set the tempdins to processed in working table
update #tempdins
set processed = 1
where processed = 0
--get new counts for variables for evaulation
select #archivecount = (select count(*) from unarchs)
select #stopsign = (select val from ##stopsign)
--wait a second so the CPU doesn't spin
waitfor delay '00:00:01'
end
else
begin
--get new counts for variables for evaulation
select #archivecount = (select count(*) from unarchs)
select #stopsign = (select val from ##stopsign)
--wait a second so the CPU doesn't spin
waitfor delay '00:00:01'
end
end
set nocount off
Here is what I have for ORACLE so far (writing in PL/SQL):
-- run the commented out statement in another query analyzer window to stop the script
-- update ##stopsign set val = 1
--if working tables exists clear them, if not create and initialize them
declare
v_sql LONG;
begin
v_sql:='CREATE GLOBAL TEMPORARY TABLE STOPSIGN;
(
VAL int
)';
execute immediate v_sql;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -955 THEN
NULL; -- suppresses ORA-00955 exception
ELSE
delete from STOPSIGN;
END IF;
END;
/
insert into STOPSIGN values (0);
--if working tables exists clear them, if not create and initialize them
declare
v_sql LONG;
begin
v_sql:='CREATE GLOBAL TEMPORARY TABLE TEMPDINS;
(
tempdin varch(255),
processed int null
)';
execute immediate v_sql;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -955 THEN
NULL; -- suppresses ORA-00955 exception
ELSE
delete from TEMPDINS;
END IF;
END;
/
--initialize #tempdins working table with all the records eligible to be unarchived
--edit the select statement if needed to change the records to be unarchived
insert into TEMPDINS(tempdin)
select * from (select tempdin
from document join packtype on packtype.packagetype=document.packagetype
and archivestatus = 'C' and ADRIVE is not null
and ADRIVE <> DRIVE ) where ROWNUM < 10;
--inialize variables with current values
Declare
archivecount int;
stopsign int;
morework int;
Begin
Select count(*) INTO archivecount from UNARCHS;
Select VAL into stopsign from STOPSIGN;
morework := 1;
END
--while there is more to do, unarchs table has less then 1000 records, and the stopsign value is 0 loop
WHILE morework > 0 and stopsign = 0
LOOP{
begin
if archivecount <1000
begin
-- number to be processed at once
-- change this value if you would like to dump more in to the unarchs table at once
set rowcount 100
update TEMPDINS
set processed = 0
where processed is null
}
--reset rowcount
set rowcount 0
--populate the unarchs table with valid values
--this will unarchive at the page (lowest) level
insert into UNARCHS (drawer,foldernumber,packageid,docid,pagenumber,unarchtype,unarchdate,unarchtime,userid,unarchdays)
select distinct drawer,foldernumber,packageid,docid,pagenumber,'Page','20061128','12:00:00','ADMIN',360
from DOCUMENT
where tempdin in (select tempdin
from TEMPDINS
where processed = 0)
--update with rowcount to see if finished
select morework = select NUM_ROWS into morework from user_tables where table_name = 'UNARCHS'
--set the tempdins to processed in working table
update TEMPDINS
set processed = 1
where processed = 0
--get new counts for variables for evaulation
select archivecount = (select count(*) from unarchs)
select stopsign = (select val from STOPSIGN)
--wait a second so the CPU doesn't spin
waitfor delay '00:00:01'
end
else
begin
--get new counts for variables for evaulation
select archivecount = (select count(*) from unarchs)
select stopsign = (select val from STOPSIGN)
--wait a second so the CPU doesn't spin
waitfor delay '00:00:01'
end
end
End
END IF
END LOOP
Any help would be appreciated. My company has no Oracle resources for me to go to and Google is getting tired of me.
I think you can get rid of everything and just do a single insert statement:
insert into unarchs (drawer,foldernumber,packageid,docid,pagenumber,unarchtype,unarchdate,unarchtime,userid,unarchdays)
select distinct drawer,foldernumber,packageid,docid,pagenumber,'Page','20061128','12:00:00','ADMIN',360
from DOCUMENT
where archivestatus = 'C'
And status = 'U';
I’m assuming you have a different process that updates the rows in the document table so this process doesn’t constantly pick up the same rows?

Not firing after update trigger when updating from insert trigger

I have two triggers on one table, one fires on insert and updates the row with additional info from other tables
CREATE TRIGGER [dbo].[eTteamTg]
ON [dbo].[entryTable]
AFTER INSERT
AS
BEGIN
UPDATE entryTable
SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId
FROM shiftTeamMemb
WHERE shiftTeamMemb.personalNumber = i.personalNumber)
FROM entryTable
INNER JOIN inserted i ON i.ID = entryTable.ID
END
And second one fires after update
CREATE TRIGGER [dbo].[eThistoryUpdTg] ON [dbo].[entryTable]
AFTER update
AS
BEGIN
INSERT INTO eThistory(my columns)
SELECT
*, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE()
FROM
deleted
END
What I need to do is to not fire AFTER UPDATE trigger when I am updating the rows with AFTER INSERT trigger. Is it possible ?
I suggest you to merge this two triggers into one:
CREATE TRIGGER [dbo].[eTteamTg]
ON [dbo].[entryTable]
AFTER INSERT, UPDATE
AS
BEGIN
UPDATE entryTable
SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId
FROM shiftTeamMemb
WHERE shiftTeamMemb.personalNumber = i.personalNumber)
FROM entryTable
INNER JOIN inserted i ON i.ID = entryTable.ID
INSERT INTO eThistory(my columns)
SELECT
*, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE()
FROM
deleted
END
Then after INSERT, it will update shiftTeam and `deleted1 will not be used.
After UPDATE, it will update shiftTeam and use deleted to get previous (even to above update) values.
Or if you don't need to update shiftTeam in case of UPDATE on this table you can add IF statement:
CREATE TRIGGER [dbo].[eTteamTg]
ON [dbo].[entryTable]
AFTER INSERT, UPDATE
AS
BEGIN
IF (SELECT COUNT(*) FROM deleted) = 0 --That means it was INSERT
BEGIN
UPDATE entryTable
SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId
FROM shiftTeamMemb
WHERE shiftTeamMemb.personalNumber = i.personalNumber)
FROM entryTable
INNER JOIN inserted i ON i.ID = entryTable.ID
END
INSERT INTO eThistory(my columns)
SELECT
*, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE()
FROM
deleted
END
Implement your second trigger like this,
CREATE TRIGGER [dbo].[eThistoryUpdTg] ON [dbo].[entryTable]
AFTER UPDATE
AS
BEGIN
INSERT INTO eThistory (my columns)
SELECT *
,HOST_Name() + ' ' + SUSER_NAME() + ' Upd'
,GETDATE()
FROM deleted d
WHERE NOT EXISTS (
SELECT TOP 1 shiftTeamMemb.teamId
FROM shiftTeamMemb
INNER JOIN inserted I ON i.personalNumber = shiftTeamMemb.personalNumber
INNER JOIN entryTable E ON E.ID = I.ID
WHERE shiftTeamMemb.personalNumber = i.personalNumber
)
END

Using TRY / CATCH to perform INSERT / UPDATE

I have this pattern in a number of stored procedures
-- Table1
[id] [int] IDENTITY(1,1) NOT NULL
[data] [varchar](512) NULL
[count] INT NULL
-- 'data' is unique, with a unique index on 'data' in 'Table1'
BEGIN TRY
INSERT INTO Table1 (data, count) SELECT #data,1;
END TRY
BEGIN CATCH
UPDATE Table1 SET count = count + 1 WHERE data = #data;
END CATCH
I've been slammed before for using this pattern
You should never have exception "catching" in your normal logic flow. (Thus why it is called an "exception"..it should be exceptional (rare). Put a exists check around your INSERT. "if not exists (select null from Data where data = #data) begin /* insert here */ END
However, I can't see a way around it in this instance. Consider the following alternative approaches.
INSERT INTO Table1 (data,count)
SELECT #data,1 WHERE NOT EXISTS
(SELECT 1 FROM Table1 WHERE data = #data)
If I do this, it means every insert is unique, but I can't 'catch' an update condition.
DECLARE #id INT;
SET #id = (SELECT id FROM Table1 WHERE data = #data)
IF(#id IS NULL)
INSERT INTO Table1 (data, count) SELECT #data,1;
ELSE
UPDATE Table1 SET count = count + 1 WHERE data = #data;
If I do this, I have a race condition between the check and the insert, so I could have duplicates inserted.
BEGIN TRANSACTION
DECLARE #id INT;
SET #id = (SELECT id FROM Table1 WHERE data = #data)
IF(#id IS NULL)
INSERT INTO Table1 (data, count) SELECT #data,1;
ELSE
UPDATE Table1 SET count = count + 1 WHERE data = #data;
END TRANSACTION
If I wrap this in a TRANSACTION it adds more overhead. I know TRY/CATCH also brings overhead but I think TRANSACTION adds more - anyone know?.
People keep telling me that using TRY/CATCH in normal app logic is BAD, but won't tell me why
Note: I'm running SQL Server 2005 on at least one box, so I can't use MERGE
Try to update and if it's failed - to insert new.
BEGIN TRANSACTION
UPDATE t
SET
t.count = t.count + 1
FROM Table1 t
WHERE t.data = #data
IF (##ROWCOUNT = 0)
BEGIN
INSERT INTO Table1
(data, count)
VALUES
(#data, 1)
END
COMMIT TRANSACTION
The explicit transaction is the cost of doing business with a conditional INSERT/UPDATE in order to address concurrency. The example below uses locking hints to a avoid race condition with this code.
BEGIN TRANSACTION;
INSERT INTO Table1
( data
, count
)
SELECT #data
, 1
WHERE NOT EXISTS ( SELECT 1
FROM Table1 WITH ( UPDLOCK, HOLDLOCK )
WHERE data = #data );
IF ##ROWCOUNT = 0
UPDATE Table1
SET count = count + 1
WHERE data = #data;
COMMIT;
If the more common path is the UPDATE, try that first followed by the conditional INSERT.

Sql Server trigger triggers with empty inserted and deleted tables

I have defined a trigger on a table that is triggered
AFTER INSERT, DELETE, UPDATE
There are cases where the trigger fires, with both INSERTED AND DELETED tables being empty. How can this be possible?
For the records, that's the trigger
CREATE TRIGGER [dbo].[AuditUsersTrigger] ON [dbo].[Users]
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #type nchar(1), #hasChanges bit
SET #hasChanges = 1
IF EXISTS (SELECT * FROM INSERTED)
IF EXISTS (SELECT * FROM DELETED)
BEGIN
SELECT #type = 'U'
IF EXISTS (
SELECT *
FROM INSERTED i
INNER JOIN DELETED d ON
i.Name = d.Name AND
i.Pwd = d.Pwd AND
...
) SELECT #hasChanges = 0
END
ELSE
SELECT #type = 'I'
ELSE
SELECT #type = 'D'
IF #type = 'D' OR (#type = 'U' AND #hasChanges = 1)
BEGIN
INSERT AuditUsers (
New, Id, Name, ...
)
SELECT
0, Id, Name, ...
FROM DELETED
IF #type = 'D'
BEGIN
INSERT AuditUsers (New)
SELECT 1
END
END
IF #type = 'I' OR (#type = 'U' AND #hasChanges = 1)
BEGIN
IF #type = 'I'
BEGIN
INSERT AuditUsers (New)
SELECT 0
END
INSERT AuditUsers (
New, Id, Name, ...
)
SELECT
0, Id, Name, ...
FROM INSERTED
END
IF Trigger_Nestlevel() < 2
BEGIN
DECLARE #clientId TABLE (id INT)
DECLARE #clientCode NVARCHAR(50), #shopId INT;
IF #type = 'I' OR #type = 'U'
BEGIN
SELECT #clientCode = ClientCode, #shopId = ShopId FROM INSERTED;
INSERT INTO #clientId SELECT id FROM Clients WHERE code = #clientCode;
IF NOT EXISTS (SELECT 1 FROM #clientId)
BEGIN
INSERT Clients (name, code, active, shopId) OUTPUT INSERTED.id INTO #clientId
VALUES (#clientCode, #clientCode, 1, #shopId);
END
UPDATE Users SET ClientId = (SELECT TOP 1 id FROM #clientId) WHERE ClientCode = #clientCode;
END
END
END
This is documented behaviour
DML triggers execute when a user tries to modify data through a data manipulation language (DML) event. DML events are INSERT, UPDATE, or DELETE statements on a table or view. These triggers fire when any valid event is fired, regardless of whether or not any table rows are affected.
If you have a recurring loop, whereby table A has a trigger that affects table B, and table B has a trigger that affects table A, you can manage this using TRIGGER_NESTLEVEL, or by checking if either inserted or deleted contain any rows before actually doing anything.

please help me out for this query

i given the countryId and countrname as 33 and india if i give the same countryid and countryname it has to give the message as updated. please tell me the code for that.. I wrote like this
IF EXISTS (SELECT * FROM Table1 WHERE CountryName=#CountryName )
BEGIN
SELECT 'already exists'
END
ELSE BEGIN
UPDATE Table1
SET
CountryName = #CountryName
WHERE CountryId = #CountryId
SELECT #QStatus = 'values updated';
END
Your IF statement logic is not correct. UPDATE is used to change existing rows, so if the row does not exist there is nothing to UPDATE.
You need to use INSERT
IF EXISTS (SELECT * FROM Table1 WHERE CountryName=#CountryName )
BEGIN
SELECT 'already exists'
END
ELSE BEGIN
INSERT Table1 (CountryName, CountryId)
VALUES (#CountryName, CountryId)
SELECT #QStatus = 'values inserted';
END
However, if what you need to do is replace the country name of the ID passed in you can use
IF EXISTS (SELECT * FROM Table1 WHERE CountryId=#CountryId )
BEGIN
UPDATE Table1
SET
CountryName = #CountryName
WHERE CountryId = #CountryId
SELECT #QStatus = 'values updated';
END
From the SQL you have provided it's hard to tell what you are actually trying to do.

Resources