Get parameter values from DBCC INPUTBUFFER - sql-server

I've created a trigger on a SQL Server 2012 table to log all INSERT, UPDATE and DELETE actions against the table to another table. I got my initial code from the blog post at http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/01/11/tsql2sday-using-sys-dm-exec-sql-text-to-get-the-calling-statement.aspx.
Here are the scripts for a test table, the table I'm logging changes on the test table to, and the trigger on the test table:
CREATE TABLE [dbo].[TestAddresses](
[RecNo] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
[Address] [varchar](50) NULL,
[City] [varchar](50) NULL,
[State] [varchar](2) NULL,
[Zip] [varchar](12) NULL
) ON [PRIMARY]
GO
-------------------------------------
CREATE TABLE [dbo].[TestAddressesLog](
[RecNo] [int] IDENTITY(1,1) NOT NULL,
[Sql] [nvarchar](max) NULL,
[Timestamp] [datetime] NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TestAddressesLog] ADD CONSTRAINT [DF_TestAddressesLog_Sql] DEFAULT (N'') FOR [Sql]
GO
ALTER TABLE [dbo].[TestAddressesLog] ADD CONSTRAINT [DF_TestAddressesLog_Timestamp] DEFAULT (getdate()) FOR [Timestamp]
GO
------------------------------------
ALTER TRIGGER [dbo].[TestAddressLogTrigger]
ON [dbo].[TestAddresses]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE #TEMP TABLE (EventType NVARCHAR(30), Parameters INT, EventInfo NVARCHAR(4000))
INSERT INTO #TEMP EXEC('DBCC INPUTBUFFER(##SPID)')
INSERT INTO TestAddressesLog (Sql)
SELECT EventInfo FROM #TEMP
END
This works great for standard CRUD operations where there are no parameters. However, when my .NET code executes a parameterized operation such as the following (assigning a value such as 'CA' to #NewState):
UPDATE TestAddresses SET State = #NewState WHERE State = 'TX'
I get the following result:
(#NewState int)UPDATE TestAddresses SET State = #NewState WHERE State = 'TX'
I need a way to get and record the value of the passed parameter. Anyone have a solution for this?

That's not going to work. The only way is to capture RPC Completed events using Extended Events, parse them and persist them.

Related

SSMS Check Constraint Does not Allow Update of Table with valid data

I have the following in my table design from the Script Table as...Create To...New Query Window of SSMS:
CREATE TABLE [DirectMarketing].[Opportunity](
[OpportunityID] [int] NOT NULL,
[ProspectID] [int] NOT NULL,
[DateRaised] [date] NOT NULL,
[Likelihood] [tinyint] NOT NULL,
[Rating] [char](1) NOT NULL,
[EstimatedClosingDate] [date] NOT NULL,
[EstimatedRevenue] [decimal](10, 2) NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
ALTER TABLE [DirectMarketing].[Opportunity] ADD CONSTRAINT [DF_Opportunity_DateRaised] DEFAULT (getdate()) FOR [DateRaised]
GO
ALTER TABLE [DirectMarketing].[Opportunity] WITH NOCHECK ADD CONSTRAINT [CK_Opportunity] CHECK (([DateRaised]=getdate()))
GO
ALTER TABLE [DirectMarketing].[Opportunity] CHECK CONSTRAINT [CK_Opportunity]
GO
But when I try to test it by entering data into the table i keep getting an error saying no row was update. Kindly see the image below for data entered and subsequent error. What am I not doing right?
The expression [DateRaised]=GETDATE() is comparing types of date and datetime. Because datetime has a higher precedence, the date value is converted to a datetime with a time of midnight. The comparison then returns false because of the non-midnight time.
The solution is to add a CAST:
ALTER TABLE [DirectMarketing].[Opportunity]
ADD CONSTRAINT [CK_Opportunity] CHECK ([DateRaised]=CAST(getdate() AS date));

How to create dynamic trigger with stored procedure in SQL Server

I am working on an audit trail using SQL Server triggers to identify inserts, updates and deletes on tables. Below are my tables and trigger:
CREATE TABLE [dbo].[AuditTrail]
(
[AuditId] [INT] IDENTITY(1,1) NOT NULL,
[DateTime] [DATETIME] NOT NULL,
[TableName] [NVARCHAR](255) NOT NULL,
[AuditEntry] [XML] NULL,
CONSTRAINT [PK_AuditTrail] PRIMARY KEY CLUSTERED
)
CREATE TABLE [dbo].[Employee]
(
[ID] [UNIQUEIDENTIFIER] NOT NULL DEFAULT (newid()),
[NameEmployee] [NVARCHAR](255) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
)
CREATE TABLE [dbo].[Transaction]
(
[ID] [UNIQUEIDENTIFIER] NOT NULL DEFAULT (newid()),
[NameTransaction] [NVARCHAR](255) NOT NULL,
CONSTRAINT [PK_Transaction] PRIMARY KEY CLUSTERED
)
CREATE TRIGGER AuditEmployee
ON [dbo].[Employee]
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF (SELECT COUNT(*) FROM deleted) > 0
BEGIN
DECLARE #AuditMessage XML
SET #AuditMessage = (SELECT * FROM deleted FOR XML AUTO)
INSERT INTO AuditTrail (DateTime, TableName, AuditEntry)
VALUES (GETDATE(), 'Simple', #AuditMessage)
END
END
GO
I have created a trigger for the employee table, but that is a static trigger. How to dynamically trigger with stored procedure depends on the number of tables we have, except the AuditTrail table?

Row update if row exists. Insert it if row doesn't exist

I'm developing a SQL SERVER 2012 express and developer solution.
I will receive an xml in an stored procedure. In the stored procedure I will parse the xml and insert its data into a table.
My problem here is that in this xml could contain data that exists on the table, and I need to update the data on the table with the new one.
I don't want to check if each row in xml exists on the table.
I think I can use IGNORE_DUP_KEY but I'm not sure.
How can I update or insert new data without checking it?
This is the table where I want to insert (or update) the new data:
CREATE TABLE [dbo].[CODES]
(
[ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
[CODE_LEVEL] [tinyint] NOT NULL,
[CODE] [nvarchar](20) NOT NULL,
[COMMISIONING_FLAG] [tinyint] NOT NULL,
[IS_TRANSMITTED] [bit] NOT NULL,
[TIMESPAN] [datetime] NULL,
[USERNAME] [nvarchar](50) NULL,
[SOURCE] [nvarchar](50) NULL,
[REASON] [nvarchar](200) NULL
CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED
(
[CODE_LEVEL] ASC,
[CODE] ASC
)
)
The "IGNORE_DUP_KEY" parameter ,is ignore inserting new row, if he is already exists, but it is not dealing with update in case it exists.
the solution to your request is by MERGE or DML operation (INSERT/UPDATE/DELETE) .
BTW,
The parameter "IGNORE_DUP_KEY" is covering existsnce for the index key only (index column).

Reuse t-sql table variable or clone its structure

I'm writing stored procedure to paginate results of a different stored procedure. I do it by executing the stored procedure and inserting results into the table variable:
DECLARE #allResults table
(
[ID] [int] NOT NULL,
[DESCRIPTION] [varchar](MAX) NULL,
[COL1] [VARCHAR],
[COL2] [VARCHAR],
...
);
INSERT #allResults Exec [dbo].[GetResults];
I need to filter the results and store them somewhere because I will use filtered results in at least two places: to count all the records and to actually paginate. Ideally I'd like to reuse the #allResults table as I won't need its content anymore after filtering. Something similar to:
#allresults = #allresults where [DESCRIPTION] like '%keyword%'
I'm not exactly sure how can I truncate table in the same moment as I filter it. That's why I created second table variable with the same structure:
DECLARE #filteredResults table
(
[ID] [int] NOT NULL,
[DESCRIPTION] [varchar](MAX) NULL,
[COL1] [VARCHAR],
[COL2] [VARCHAR],
...
);
It's not a bad solution, and it works. But I wonder could I reuse the definition of a table variable? Something that would look like:
DECLARE #filteredResults, #allResults table
(
[ID] [int] NOT NULL,
[DESCRIPTION] [varchar](MAX) NULL,
[COL1] [VARCHAR],
[COL2] [VARCHAR],
...
);
Is there a way to do it? Maybe there's a way to clone table variable? I guess simultaneous delete and filtering could be achieved using delete with output clause but I'm not exactly sure how should I write it.
I hate to repeat code. Maybe there's a simple solution you know of :)
You can create your own user table type:
CREATE TYPE dbo._t_test AS TABLE(
[ID] [int] NOT NULL,
[DESCRIPTION] [varchar](MAX) NULL,
[COL1] [VARCHAR],
[COL2] [VARCHAR]
)
GO
And then create table variables like this:
DECLARE #filteredResults dbo._t_test
, #allResults dbo._t_test

create update trigger

I have table with 2 columns: id and name. need trigger which at change of records in this table would bring values after updating in other table.
Something like:
CREATE TRIGGER [dbo].[trigger_tablename] -- replace 'tablename' with your table name
ON [dbo].[tablename] FOR UPDATE -- replace 'tablename' with your table name
AS
BEGIN
insert into T_tablename_Monitor -- replace 'tablename' with your table name
select NewID(),ID, Name,'After Update',SUSER_SNAME(), getdate() from inserted
END
The monitor table might look like:
CREATE TABLE [dbo].[T_tablename_Monitor]( -- replace 'tablename' with your table name
[Row_ID] [varchar](36) NOT NULL,
[ID] [varchar](30) NOT NULL, -- replace with your type
[Name] [varchar](50) NULL, -- replace with your type
[Action] [varchar](50) NOT NULL,
[UserName] [varchar](100) NOT NULL,
[CTime] [datetime] NOT NULL
) ON [PRIMARY]
Here is how to create an update trigger

Resources