Trigger syntax for sql server - sql-server

I have realized that the syntax for triggers is slightly different for different software. What would be the syntax for the following piece of code in SQL Server 2012?
Create trigger before_playercatalogue_update
before update
on player_catalogue
For each row
Begin
Insert into player_audit
set action = 'update',
playerid = old.playerid
fname = old.fname,
datachange = (Now);
End

The syntax is quite different. It would look like:
Create trigger after_playercatalogue_update
on player_catalogue after update
as
Begin
Insert into player_audit(action, playerid, fname, datachange)
select 'update', playerid, fname, getdate()
from inserted;
End;
Note some of the changes:
This is an after trigger. SQL Server doesn't have "before" triggers.
The set clause is not supported for insertin SQL Server (or other databases).
SQL Server does not have "new" and "old". It uses inserted, a view on the records that have changed.

Related

Convert SQL Server trigger to Oracle and master.dbo.sysprocesses - not duplicate

I tried searching but could not find exactly what I'm looking for. I'm new to SQl Server and involved into SQL Server to Oracle conversion, and it is manual conversion. All I have is SQL Server files.
I see two types of SQL Server triggers - FOR UPDATE and FOR INSERT. They look to me as before update and before insert triggers in Oracle. I'd like to confirm this please and if you can provide examples that would be great.
Also, what is the equivalent to master.dbo.sysprocesses in Oracle please? Is this v$session? I can get user from dual in Oracle. Is this what nt_username is in below code?
Here is typical code examples I need to convert to Oracle - is this before insert?
CREATE TRIGGER trigger_name ON dbo.table_name
FOR Insert AS
declare #InsertUser varchar(32)
BEGIN
SELECT #InsertUser = nt_username from master.dbo.sysprocesses where spid = ##spid
Update table_name
SET dCreateDate = GETDATE(), cCreateUser = #InsertUser
FROM table1 a ,table2 i WHERE a.tab_id = i.tab_id
END
GO
Update Trigger - before update?
CREATE TRIGGER trigger_name ON dbo.table_name
FOR UPDATE AS
declare #UpdateUser varchar(32)
if not update(CreateUser) and not update(CreateDate)
BEGIN
SELECT #UpdateUser = nt_username from master.dbo.sysprocesses where spid = ##spid
Update table_name
SET UpdateDate = GETDATE(), UpdateUser = #UpdateUser
FROM table1 a ,table2 i WHERE a.tab_id = i.tab_id
END
GO
Should I combine these two into if inserting... elsif updating in Oracle?
Thank you very much to all.

SQL Server trigger firing 3 times

I have set up the following audit trigger to create copies of the record changes into my change log (should we ever need to see what happened to our data). The problem is every time I update the record, it creates 3 records in my log table.
Below you can see the result set and my code I have been using.
Code:
/* ==Scripting Parameters==
Source Server Version : SQL Server 2016 (13.0.4446)
Source Database Engine Edition : Microsoft SQL Server Standard Edition
Source Database Engine Type : Standalone SQL Server
Target Server Version : SQL Server 2017
Target Database Engine Edition : Microsoft SQL Server Standard Edition
Target Database Engine Type : Standalone SQL Server
*/
USE [STONE_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_set_for_update_audit_fields]
ON [dbo].[permission_types]
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO STONE_DB_CHANGE_LOGS.dbo.permission_types_log ([permission_type_id], [business_process], [active], [created_at], [created_by], [change_type], [change_user], [change_at])
SELECT DISTINCT
inserted.id,
inserted.[business_process], inserted.[active],
inserted.[created_at], inserted.[created_by],
'U', SYSTEM_USER, GETDATE()
FROM
inserted
WHERE
1 = 1
AND inserted.[created_at] < getdate();
UPDATE [dbo].[permission_types]
SET updated_at = GETDATE(), updated_by = SYSTEM_USER
FROM [dbo].[permission_types]
INNER JOIN inserted ON permission_types.id = inserted.id
WHERE 1 = 1
--AND permission_types.updated_at < getdate()
;
PRINT 'Record Updated'
END
You need to prevent a database trigger from recursing
Check this solution
How do I prevent a database trigger from recursing?
You should add at the begin of trigger code like this:
IF NOT UPDATE(business_process)
RETURN;
More info: UPDATE()
USE the following code to avoid nesting
IF((SELECT trigger_nestlevel() ) > 1)
RETURN

How to Automate Query Results in Microsoft SQL Management Studio 2012 and send the query results to WCF service as Instant Notifications?

I am new to SQL Server. I need track the Data Changes in Database Table inside the Microsoft SQL Server Management Studio.If any Data insertion,Deletion and modification done..in Table ,the WCF service should receive notifications from SQL Server 2012 Database.
Is this possible to send instant notifications of Data Table from SQL server to WCF Service?
Done's and Studied:-
I am already refer some websites. I have an Idea about Change Data Capture and Change Tracking procedure in SQL Server.Then i were followed the Change Data Capture Procedure to Track the Data in SQL Server.Then i look over some information about Jobs in SQL Server Data Agent.With the help of jobs,possible to automate a query execution in sql server with a time interval?
Then i am created a Database in SQL server using DML and enabled the Change Data Capture Feature for Database and Table Level,specified number of Columns!
My New Database:-
CREATE DATABASE Testcdc_feature
GO
Use Testcdc_feature;
GO
CREATE TABLE dbo.one(
Name varchar(100),
Id int,
Designation varchar(100),
Salary int,
);
Enabled Change Data Capture Feature for Database:-
use Testcdc_feature;
GO
-- enable CDC on the database
EXEC sys.sp_cdc_enable_db;
GO
Enabled CDC Feature for The Table and Specified Columns:-
USE Testcdc_feature;
GO
EXEC sys.sp_cdc_enable_table
#source_schema = N'dbo',
#source_name = N'one',
#role_name = NULL, -- Role gating not enabled
#filegroup_name = N'PRIMARY', -- should consider writing audit date to separate filegroup
#captured_column_list = N'Name,Id,Designation,Salary';
GO
Inserted Values to the Table Columns:-
USE Testcdc_feature;
GO
SET NOCOUNT ON;
INSERT INTO dbo.one VALUES(
'thirunavukkarasu',2345,'TeamLeader',12000000
);
INSERT INTO dbo.one VALUES(
'Selva',3456,'Developer',30000);
INSERT INTO dbo.one VALUES(
'John',9876,'Application Tester',45000
);
INSERT INTO dbo.one VALUES(
'Anand',56789,'Developer',56000
);
INSERT INTO dbo.one VALUES(
'Priya',6709,'Developer',78000
);
INSERT INTO dbo.one VALUES(
'Swetha',8907,'Developer',100000
);
For Check the Data Changes the query is written as below(DEMO):-
USE Testcdc_feature;
GO
DECLARE #from_lsn binary(10), #to_lsn binary(10)
SET #from_lsn = sys.fn_cdc_get_min_lsn('dbo_one')
SET #to_lsn = sys.fn_cdc_get_max_lsn()
SELECT
CT.__$start_lsn,
CT.__$operation,
CASE CT.__$operation
WHEN 1 THEN 'Delete'
WHEN 2 THEN 'Insert'
WHEN 3 THEN 'Update - Pre'
WHEN 4 THEN 'Update - Post'
END AS Operation,
CT.*,
LSN.tran_begin_time,
LSN.tran_end_time,
LSN.tran_id
FROM
cdc.fn_cdc_get_all_changes_dbo_one
(#from_lsn, #to_lsn, N'all update old') AS CT INNER JOIN
cdc.lsn_time_mapping AS LSN ON CT.__$start_lsn = LSN.start_lsn
GO
When Executing the Last Query i got output as below
I need to automate the last Query (Demo) for a time interval and send it results as instant notifications of Data modifications from SQL server to WCF Service?
The WCF Should Receive the Insertion,Modification and Deletion of Data From SQL server.is this possible?
My Question:-
1.How to automate a query execution with a time interval in MS SQL Server 2012?
2.How to send the automated query results into wcf service?
How do i achieve this? Could anyone provide me a idea or solution?
It Might me more Helpful!
From what I understand.
Your needs are to communicate to a WCF Service any transactions done to a table with a time interval.
What I recommand you is to create a trigger on your table that will insert in a specific table for transaction informations that you need (columns changed? is it INSERT/UPDATE/DELETE? previous and new values? a status flag?).
After that, you set up a new SQL job with your time constraints that will raise your WCF Service. Finally, your WCF Service will read any news data in your new transaction table.
Here is a link to setting up a Job.
EDIT : You will probably need indexes on your transaction tables particularly on the previous tables key and on your status flag. What a mean by a status flag is information related to your WCF Service (did it already read this line? If you implements a re-processing in case of error, which step this line is on your WCF Service?)
When your WCF Service will read your transaction table, to do it properly you may use transactions in order to re-process from the start. Here is a link to use transactions. I don't recommand this since your transaction table will be highly accessed and using a transaction will put locks on your table if not use properly.
Or you may Update your status flag and retrieve the data with an OUTPUT clause in order to re-process step-by-step. Here is a link to use output clause on update. (The example you are looking for is C.)
By what I am understanding, all you want is an event triggering when you
"If any Data insertion,Deletion and modification done..in Table"
what you can do is create SQL triggers, you can set them to trigger(throw an event) when you delete from, update or insert into the table.

How do I make ALTER COLUMN idempotent?

I have a migration script with the following statement:
ALTER TABLE [Tasks] ALTER COLUMN [SortOrder] int NOT NULL
What will happen if I run that twice? Will it change anything the second time? MS SQL Management Studio just reports "Command(s) completed successfully", but with no details on whether they actually did anything.
If it's not already idempotent, how do I make it so?
I would say that second time, SQL Server checks metadata and do nothing because nothing has changed.
But if you don't like possibility of multiple execution you can add simple condition to your script:
CREATE TABLE Tasks(SortOrder VARCHAR(100));
IF NOT EXISTS (SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE [TABLE_NAME] = 'Tasks'
AND [COLUMN_NAME] = 'SortOrder'
AND IS_NULLABLE = 'NO'
AND DATA_TYPE = 'INT')
BEGIN
ALTER TABLE [Tasks] ALTER COLUMN [SortOrder] INT NOT NULL
END
SqlFiddleDemo
When you execute it the second time, the query gets executed but since the table is already altered, there is no effect. So it makes no effect on the table.
No change is there when the script executes twice.
Here is a good MSDN read about: Inside ALTER TABLE
Let's look at what SQL Server does internally when performing an ALTER
TABLE command. SQL Server can carry out an ALTER TABLE command in any
of three ways:
SQL Server might need to change only metadata.
SQL Server might need to examine all the existing data to make sure
it's compatible with the change but then change only metadata.
SQL Server might need to physically change every row.

The multi-part identifier "inserted.Id" could not be bound while creating trigger

I am trying to create a trigger on a table in SQL Server 2012, but it is giving an error like below
"The multi-part identifier "inserted.Id" could not be bound",
The query I am executing is
CREATE TRIGGER dbo.[TR_t_documents_InsertUpdateDelete] ON
dbo.[t_documents] AFTER INSERT, UPDATE, DELETE
AS
BEGIN
UPDATE dbo.[t_documents]
SET dbo.[t_documents].[UpdatedAt] = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME())
FROM
INSERTED
WHERE inserted.[Id] = dbo.[t_documents].[Id]
END
The same is executing successfully in SQL Server 2014.
Can anyone help me why this is happening in SQL server 2012?
This is due to the collation you have for the database. In this case, you are using a case sensitive collation, so the table names need to be consistently. For the virtual trigger tables, these need to be in upper case, for example:
CREATE TRIGGER dbo.[TR_t_documents_InsertUpdateDelete] ON
dbo.[t_documents] AFTER INSERT, UPDATE, DELETE
AS
BEGIN
UPDATE dbo.[t_documents]
SET dbo.[t_documents].[UpdatedAt] = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME())
FROM
INSERTED
WHERE INSERTED.[Id] = dbo.[t_documents].[Id]
-- ^^^^^^^^
-- THIS!
END

Resources