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.
Related
I have a database that uses Insert, Update, and Delete Triggers for almost all tables. They log the host and program performing the operation in a separate auditing table. The triggers all include this select statement to set variables that get inserted into the auditing table:
select #HostName = HostName, #ProgramName = Program_Name
from master..sysprocesses where SPID = ##SPID
We are now looking to migrate to Azure SQL Database, which does not support the master..sysprocesses syntax. It also appears that table is deprecated as well: https://learn.microsoft.com/en-us/sql/relational-databases/system-compatibility-views/sys-sysprocesses-transact-sql?view=sql-server-ver15
What we need to do is update the triggers to use this instead:
select #HostName = [host_name], #ProgramName = [program_name]
from sys.dm_exec_sessions where session_id = ##SPID
However, the database has hundreds of tables and each table has three triggers that need updating. The text-replacement for each trigger is identical. Is there a feasible way to script out something to perform this update on all triggers in the database?
OK, I just tested this by jamming your string in a few triggers (as a comment of course) and then running it. I am not advocating this as the correct way to do it, as this link will help you with the correct way to do dynamic sql https://dba.stackexchange.com/questions/165149/exec-vs-sp-executesql-performance
However, this does work and will help you understand how you would piece these things together to get to that point.
Note, any formatting difference between your triggers may cause this to miss some, so youll want to verify that 0on your own.
DECLARE #string VARCHAR(8000)='select #HostName = HostName, #ProgramName = Program_Name
from master..sysprocesses where SPID = ##SPID'
, #counter INT=1
, #Max INT
, #Sql VARCHAR(mAX)
;
IF OBJECT_ID('TempDB..#TrigUpdate') IS NOT NULL DROP TABLE #TrigUpdate;
CREATE TABLE #TrigUpdate
(
SqlVar VARCHAR(MAX)
, RowID INT
)
;
INSERT INTO #TrigUpdate
SELECT REPLACE(REPLACE(t.definition, #string, ''), 'CREATE TRIGGER', 'ALTER TRIGGER')
, Row_Number() OVER (ORDER BY t.Definition ASC) AS RowID
FROM sys.objects o
INNER JOIN sys.sql_modules t on o.object_id =t.object_id
WHERE o.type_desc='SQL_TRIGGER'
AND CHARINDEX(#string, t.definition,1)>0
;
SET #Max = (SELECT COUNT(*) FROM #TrigUpdate);
WHILE #Counter<=#Max
BEGIN
SET #sql = (SELECT SqlVar FROM #TrigUpdate WHERE RowID=#counter);
EXEC(#Sql);
SET #Counter=#Counter+1;
END
It could be done with Object_Definition and Replace.
Create Table #Triggers_new (TriggerName sysname, QueryText VarChar(max))
Declare #string_pattern VarChar(max), #string_replacement VarChar(max)
Select #string_pattern = '<string_pattern>'
Select #string_replacement = '<string_replacement>'
Insert Into #Triggers_new (TriggerName, QueryText)
Select [name], Replace(Object_Definition(object_id), #string_pattern, #string_replacement)
From sys.objects
Where [type] = 'TR'
Order by [name]
-- Update #Triggers_new Set QueryText = Replace(QueryText, 'Create Trigger ', 'Alter Trigger ')
Why do you use a so heavy query on system table/view that can be changed without your consent ?
Can't you simplify you by using metada functions like :
SELECT HOST_NAME(), PROGRAM_NAME()...
That will give the requested information values ?
I have some mysterious problem where every day one table in DB (SQL Server 2016) is being recreated (I suppose dropped and created) with old data. I checked various options to try to find what process is doing this, however was unable to do that.
Scheduled Tasks - nothing
SQL Agent Jobs - nothing
How to trace what user/application/anythingelse is doing this ?
I tried launching SQL Profiler and starting manual trace, but after some time (half a day or so) it just stopped.
The default trace captures schema changes. Review the Schema Change History report or run the query below to retrieve the info in T-SQL. Note that the default trace rollover files are limited to 5 files of up to 20MB each so older events may have rolled off.
--select object created and deleted information from default trace
SELECT
trace_table.StartTime
, te.name
, trace_table.ObjectName
, trace_table.ApplicationName
, trace_table.LoginName
FROM (
SELECT REVERSE(SUBSTRING(REVERSE(path), CHARINDEX('\', REVERSE(path)) , 255)) + 'log.trc'
FROM sys.traces
WHERE
is_default = 1
) AS trace(path)
CROSS APPLY sys.fn_trace_gettable(trace.path, DEFAULT) AS trace_table
JOIN sys.trace_events AS te ON
te.trace_event_id = trace_table.EventClass
WHERE
EventSubClass = 0
AND name IN('Object:Created', 'Object:Deleted')
ORDER BY StartTime;
create a database trigger and log the create/drop table events:
create table dbo.traceTabledropcreate(EventDataXML xml, LogDatetime datetime default(getdate()));
go
create or alter trigger dbtrigger_traceTabledropcreate
on database
with execute as 'dbo'
for CREATE_TABLE, DROP_TABLE
as
begin
set nocount on;
--insert into dbo.traceTabledropcreate(EventDataXML)
--values (EVENTDATA());
declare #sessionxml xml =
(
select EVENTDATA(),
(
select *
from sys.dm_exec_sessions
where session_id = ##spid
for xml path('sessioninfo'), type
)
for xml path('')
);
insert into dbo.traceTabledropcreate(EventDataXML)
values (#sessionxml);
end
go
---..... and wait....
--..test
create table dbo.testtable(id int)
go
select *
from dbo.traceTabledropcreate
go
drop table dbo.testtable
go
select *
from dbo.traceTabledropcreate
go
Is there a way of getting the SQL code that fired a trigger from inside the fired trigger, without using DBCC INPUTBUFFER or sys.dm_exec_input_buffer?
I need this for a trigger that logs the new value, the old value and the statement that made the change in that table.
Even though DBCC INPUTBUFFER resolves the challenge, I cannot use it because I need to use "INSERT INTO ... EXEC" in order to get the query that fired the trigger and the trigger is fired by many statements that already use "INSERT INTO ... EXEC", so I will get the error
An INSERT EXEC statement cannot be nested
From my research, sys.dm_exec_input_buffer might do the trick, but I cannot use it since it is available only for SQL Server 2014 SP4 and newer (as mentioned here: Get last command in SQL Server without DBCC INPUTBUFFER), and I am using an older version.
I have tried several ways of solving the problem but without success. I cannot get the SQL statement that fired the trigger but only the last executing statement which is the trigger.
To see the problem, take a look at the following code:
--Create the table that will have the trigger
CREATE TABLE [dbo].[___testTrigger]
(
[text] [NVARCHAR!(50) NOT NULL
) ON [PRIMARY]
GO
CREATE TRIGGER dbo.TestTriggerAuditLog
ON dbo.___testTrigger
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON;
--Version 1: without "INSERT INTO ... EXEC" but does not get the text of the statement that fired the trigger. Instead, it gets the current running query, which is the trigger
SELECT sqltext.TEXT,
req.session_id,
req.status,
req.command,
req.cpu_time,
req.total_elapsed_time
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
WHERE req.session_id = ##SPID
--Version 2: gets the statement that fired the trigger, but we need to use "INSERT INTO ... EXEC"
DECLARE #inputbuffer TABLE (EventType NVARCHAR(30),Parameters INT,EventInfo NVARCHAR(4000))
INSERT INTO #inputbuffer EXEC('dbcc inputbuffer('+##Spid+') WITH NO_INFOMSGS')
SELECT * FROM #inputbuffer AS I
END
I know that in a trigger is not ok to have SELECT statements! I did it just to make the example simpler.
Now, we can insert some data to see what we get:
--test
INSERT INTO dbo.___testTrigger (text)
VALUES (N'This is a test test')
We will get the 2 selects returning different results, as can be seen in the bellow image.
Any ideas of what could I use to get the same result as DBCC INPUTBUFFER but without using "INSERT INTO ... EXEC" and without using sys.dm_exec_input_buffer as it is not available in my SQL Server version?
create table dbo.abcd(id int);
go
create trigger dbo.triggerabc on dbo.abcd for insert, update, delete
as
begin
declare #t table(query nvarchar(4000));
insert into #t (query)
select EventInfo
from OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;',
'
declare #spid nvarchar(10), #sql nvarchar(1000);
select #spid = cast(session_id as nvarchar(10))
from sys.dm_exec_requests
where session_id > 50
and wait_type = ''OLEDB''
and wait_resource like ''SQLNCLI%(SPID='' + cast(##spid as varchar(10)) + '')'';
select #sql = ''dbcc inputbuffer('' + #spid + '') WITH NO_INFOMSGS'';
exec(#sql) with result sets( (EventType NVARCHAR(30),Parameters SMALLINT,EventInfo NVARCHAR(4000)) );
'
) ;
select * from #t;
end
go
insert into abcd(id) values(123)
go
insert into abcd(id)
exec('select 456')
go
drop table abcd
go
Here's a very simple solution.
But first, since triggers don't fire on select it probably isn't very accurate to refer to "queries" firing the trigger. It would probably be more accurate to call them "statements."
Anyway, add a column to your table such as StatementName varchar(10) and then in each insert statement that will fire the trigger, add a value such as 'Statement1', 'Statement2', etc.
Then the trigger can just check the inserted row and know what statement fired the trigger.
I want to migrate data from one database to another. I have written my sql statements to do so. However, the issue I a, facing is, I need to "use databasename GO" before executing the query (Can I fire use B and A at the same time to run my script?). Below is my query.
Use B GO
Use A GO
IF NOT EXISTS (select 1 FROM B.INFORMATION_SCHEMA.TABLES where table_schema = 'dbo' and table_name = 'Animal')
BEGIN
PRINT 'Table does not exist'
RETURN
END
GO
IF NOT EXISTS (select 1 FROM B.INFORMATION_SCHEMA.TABLES where table_schema = 'dbo' and table_name = 'Animal')
BEGIN
PRINT 'Table does not exist'
RETURN
END
GO
SET IDENTITY_INSERT B.[dbo].Animal ON GO
INSERT INTO B.[dbo].Animal
(Id, Name)
SELECT (Id, Name)
From A.dbo.Animal
GO
SET IDENTITY_INSERT B.[dbo].Animal OFF
GO
Would this work to move the data?
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.