I'm trying to implement a trigger to audit the updates made to a table. In the trigger I create a temp table to store the results of a call to DBCC InputBuffer:
CREATE TABLE #inputbuffer (
EventTypevarchar(255),
Parametersint,
EventInfovarchar(255)
)
INSERT INTO #inputbuffer
exec('dbcc inputbuffer(' + ##spid + ') WITH NO_INFOMSGS ')
But there are a lot of SP's that make some Insert Exec statements and update the table with the trigger, so error 'An INSERT EXEC statement cannot be nested.' is shown.
Is there a way to catch the results of inputbuffer other than an Insert Exec? sys.dm_exec_sql_text cannot be used because it requires VIEW SERVER STATE permission on the server for user who triggers trigger.
Related
I would like to know what would be a safe way to insert data over a linked/local server into a empty copy of the database(This database will have the same table structure) using "INSERT INTO SELECT"?
I will be getting data from the local server and this data will be inserted over a linked server or local server to an archive DB.
This process needs to happen through Dynamic SQL as I am writing a Generic script.
If you have a sample script that you can supply to me then I would appreciate it.
EXAMPLE
EXEC [server_name].[OI_OAF_Archive_20210218_20210222].[dbo].sp_executesql N'SET IDENTITY_INSERT [OI_OAF_Archive_20210218_20210222].dbo.[ENT_Entry] ON'
EXEC [server_name].[OI_OAF_Archive_20210218_20210222].[dbo].sp_executesql N'ALTER TABLE [OI_OAF_Archive_20210218_20210222].dbo.[ENT_Entry] NOCHECK CONSTRAINT ALL'
IF (EXISTS (SELECT * FROM [OI_OAF_Archive_20210218_20210222].INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'ENT_Entry'))
BEGIN
PRINT 'Inserting into the table ENT_Entry'
INSERT INTO [server_name].[OI_OAF_Archive_20210218_20210222].dbo.[ENT_Entry] (entryID,details,createdByID,createdDate,lastModifiedByID,lastModifiedDate,localityID,refNumber,entryDate,gps,reloadEscStep,createdAt,reloadOccurrenceChecklistRule) SELECT entryID,details,createdByID,createdDate,lastModifiedByID,lastModifiedDate,localityID,refNumber,entryDate,gps,reloadEscStep,createdAt,reloadOccurrenceChecklistRule FROM OPENQUERY([server_name], 'SELECT entryID,details,createdByID,createdDate,lastModifiedByID,lastModifiedDate,localityID,refNumber,entryDate,gps,reloadEscStep,createdAt,reloadOccurrenceChecklistRule FROM TMP_Archive_ENT_Entry_70CDC91A ');
END
EXEC [server_name].[OI_OAF_Archive_20210218_20210222].[dbo].sp_executesql N'SET IDENTITY_INSERT [OI_OAF_Archive_20210218_20210222].dbo.[ENT_Entry] OFF'
EXEC [server_name].[OI_OAF_Archive_20210218_20210222].[dbo].sp_executesql N'ALTER TABLE [OI_OAF_Archive_20210218_20210222].dbo.[ENT_Entry] CHECK CONSTRAINT ALL'
This Example is where I INSERT the DATA from The archive server, but I want to insert it from the local server into the archive server, would that be possible?
We have two sql servers(e.g server1 & server2). We have created table on server1 and used as linkserver on server2.Also we have trying to create trigger on that table via server2 but its throws an error.So please let me know how to create insert trigger using linkserver?
exec ('
use mydb;
create table dbo.testtable(id int);
exec(''
create trigger testtrigger on dbo.testtable
for insert
as
begin
select object_name(##procid), *
from inserted;
end
'');
') at linked_server_name;
go
exec ('insert into mydb.dbo.testtable(id) values (1), (2), (3)') at linked_server_name;
go
exec ('drop table mydb.dbo.testtable') at linked_server_name;
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 am trying to insert the data of a stored procedure into a temp table like below
CREATE TABLE #CustomTable3HTML
(
ItemId varchar(30),
ItemId1 varchar(30)
)
INSERT INTO #CustomTable3HTML
EXEC SalesDeals.dbo.prGetDealProposalDetail 17100102, 1
but I am getting this error
Msg 8164, Level 16, State 1, Procedure prGetDealProposalDetail, Line 138 [Batch Start Line 1]
An INSERT EXEC statement cannot be nested.
I figured this is because the stored procedure already has an insert into clause defined and I found out that it can be used only once in the calling chain.
So I started looking for other options and found out about OpenRowSet which I am using as below
SELECT *
INTO #CustomTable3HTML
FROM OPENROWSET('SQLOLEDB','Server=Demo\Demo;Trusted_Connection=Yes;Database=SalesDeals',
'SET NOCOUNT ON;SET FMTONLY OFF;EXEC SalesDeals.dbo.prGetDealProposalDetail 17100102,1')
I am getting an error when I run this SQL command
Access to the remote server is denied because no login-mapping exists.
It works fine when I use a higher level account like sysadmin but fails with the other account which is a normal db owner on the database where I am running this SQL.
There is work around of this. It's not beautiful, but it will work.
In our outer query define a table:
CREATE TABLE #CustomTable3HTML
(
ItemId varchar(30),
ItemId1 varchar(30)
)
Change the procedure adding the following code at the end:
IF OBJECT_ID('tempdb..#CustomTable3HTML')
BEGIN
INSERT INTO #CustomTable3HTML
SELECT ....
END
ELSE
BEGIN
SELECT ....
END
After executing the stored procedure you will have the data in table.
I have two different SQL 2008 servers, I don't have permission to create a linked server in any of them.
i created a trigger on server1.table1 to insert the same record to the remote server server2.table1 using OPENROWSET
i created a stored procedure to insert this record, and i have no issue when i execute the stored procedure. it insert the recored into the remote server.
the problem is when i call the stored procedure from trigger, i get an error message.
can anyone help me please to solve this issue
Stored Procedure:
USE [DB1]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter PROCEDURE [dbo].[InsertIntoRemoteTable]
#Description nvarchar(50)
AS
insert into
OPENROWSET(
'SQLNCLI', 'Server=Server2;UID=MySRV2user;PWD=MySRV2Password',
'SELECT Description FROM [RemoteDB].[dbo].[Table_1]')
SELECT #Description
Trigger:
ALTER TRIGGER [dbo].[InsertTable1]
ON [DB1].[dbo].[Table_1]
for insert
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Desc nvarchar(50)
Select #Desc = i.Descr from INSERTED i;
EXEC InsertIntoRemoteTable #Desc
END
When i try to insert a new description value in the table i got the following error message:
"No row was updated
the data in row 1 was not committed
Error source .Net SqlClient Data provider.
Error Message: the operation could not be performed because OLE DB
provider "SQLNCLI10" for linked server "(null)" returned message "The partner transaction manager has disabled its support for remote/network transaction.".
correct the errors entry or press ESC to cancel the change(s).
can anyone help please on this
thanks
I think you have over complicated this. There is really no need for a stored procedure here to do this. You can do this insert into another database directly in your trigger with a simple insert statement. This removes a ton of complexity and it is set based.
ALTER TRIGGER [dbo].[InsertTable1]
ON [DB1].[dbo].[Table_1]
for insert
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO RemoteDB].[dbo].[Table_1](Description)
Select i.Descr
from INSERTED i;
END