Log table using trigger - sql

I need to create trigger which insert into dbo.Log table values:
- in action_type : how many records was UPDATED ,DELETED or INSERTED
- in datetime_of_action ofcourse timestamp of action
For now I have this:
DROP TABLE dbo.Log
CREATE TABLE dbo.Log (
logid INT NOT NULL IDENTITY,
action_type NVARCHAR(50) NOT NULL,
datetime_of_action DATETIME NOT NULL,
CONSTRAINT PK_Log PRIMARY KEY(logid));
CREATE TRIGGER trig2
ON Sales.Customers
FOR UPDATE , DELETE, INSERT
AS
BEGIN
......................
END
SELECT * FROM dbo.Log
And this is a script of Sales.Customers table:
CREATE TABLE [Sales].[Customers](
[custid] [int] IDENTITY(1,1) NOT NULL,
[companyname] [nvarchar](40) NOT NULL,
[contactname] [nvarchar](30) NOT NULL,
[contacttitle] [nvarchar](30) NOT NULL,
[address] [nvarchar](60) NOT NULL,
[city] [nvarchar](15) NOT NULL,
[region] [nvarchar](15) NULL,
[postalcode] [nvarchar](10) NULL,
[country] [nvarchar](15) NOT NULL,
[phone] [nvarchar](24) NOT NULL,
[fax] [nvarchar](24) NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
If anyone knows how do this I am open on your advice.

Use this query:
CREATE TRIGGER trig2
ON Sales.Customers
AFTER UPDATE , DELETE, INSERT
AS
BEGIN
IF EXISTS (select * From inserted)
BEGIN
IF EXISTS (select * From deleted)
BEGIN
INSERT INTO dbo.Log (action_typ, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM deleted) as VARCHAR(50) + ' records have been updated',GETDATE())
END
ELSE
BEGIN
INSERT INTO dbo.Log (action_typ, TOTAL, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM inserted) as VARCHAR(50) + ' records have been iniserted',GETDATE())
END
ELSE
BEGIN
INSERT INTO dbo.Log (action_typ, TOTAL, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM deleted) as VARCHAR(50) + ' records have been deleted',GETDATE())
END
END

CREATE TRIGGER trig2
ON Sales.Customers
FOR UPDATE , DELETE, INSERT
AS
BEGIN
SET NOCOUNT ON;
-- First determine the action
DECLARE #Action NVARCHAR(50);
SET #Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
THEN N'Update: ' + CAST( (SELECT COUNT(*) FROM INSERTED) AS NVARCHAR(10))
WHEN EXISTS(SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
THEN N'Insert: ' + CAST( (SELECT COUNT(*) FROM INSERTED) AS NVARCHAR(10))
WHEN EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
THEN N'Delete: ' + CAST( (SELECT COUNT(*) FROM DELETED) AS NVARCHAR(10))
ELSE NULL -- Skip. It may have been a "failed delete".
END)
INSERT INTO dbo.[Log] (action_type, datetime_of_action)
VALUES (#Action , GETDATE());
END

Related

Run large compare in Batches

I need to compare large tables with millions of rows and insert the differences into a log table.
The problem is that stored proc grows (LDF), and the database disk space is limited.
I know that commit will write the LDF to the MDF.
How can I perform the following compare in batches, and commit every hundred thousand rows?
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.CustomerLog
( OnlineStore ,
PhoneNumber ,
ChangeType
)
SELECT 'Online Store a' ,
AreaCode + PhoneNumber ,
'Added'
FROM dbo.StoreAList a
WHERE NOT EXISTS ( SELECT 1
FROM dbo.StoreAListCompare b
WHERE ( b.AreaCode + b.PhoneNumber ) = ( a.AreaCode
+ a.PhoneNumber ) );
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH;
CREATE TABLE [dbo].[StoreAList](
[ListID] [bigint] IDENTITY(1,1) NOT NULL,
[AreaCode] [char](3) NOT NULL,
[PhoneNumber] [char](7) NOT NULL,
[RecordDate] [datetime] NULL CONSTRAINT [DF_StoreAList_RecordDate] DEFAULT (getdate())
) ON [PRIMARY]
CREATE TABLE [dnc].[StoreAListCompare](
[ListID] [BIGINT] IDENTITY(1,1) NOT NULL,
[AreaCode] [CHAR](3) NOT NULL,
[PhoneNumber] [CHAR](7) NOT NULL,
[RecordDate] [DATETIME] NULL DEFAULT (GETDATE())
) ON [PRIMARY]
You can use the ##rowcount system variable and do the insert in batches until ##rowcount hits 0.
Note the added AND NOT EXISTS in dbo.CustomerLog...
Ex:
DECLARE #BATCHSIZE INT=100000
WHILE #BATCHSIZE>0
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.CustomerLog
( OnlineStore ,
PhoneNumber ,
ChangeType
)
SELECT TOP(#BATCHSIZE)
'Online Store a' ,
AreaCode + PhoneNumber ,
'Added'
FROM dbo.StoreAList a
WHERE NOT EXISTS ( SELECT 1
FROM dbo.StoreAListCompare b
WHERE ( b.AreaCode + b.PhoneNumber ) = ( a.AreaCode + a.PhoneNumber ) )
AND NOT EXISTS (SELECT 1
FROM dbo.CustomerLog CL
WHERE 'Online Store a'=CL.OnlineStore
AND AreaCode + PhoneNumber=CL.PhoneNumber
AND 'Added'=CL.ChangeType);
SET #BATCHSIZE=##ROWCOUNT
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #BATCHSIZE=0
END CATCH;
END

Bypass duplicate record when one table data is inserted from another in SQL Server

I am trying to insert data from one database table to another database table. This work is performed very well but need bypass that duplicate data cannot be inserted. Here is my query below. How can I check duplicate record?
;WITH ABC AS (
SELECT
5 AS DeviceID
, nUserID AS CardNo
, CONVERT(DATE, dbo.fn_ConvertToDateTime(nDateTime)) AS InOutDate
, CONVERT(VARCHAR(8) ,CONVERT(TIME,dbo.fn_ConvertToDateTime(nDateTime))) AS InOutTime
FROM [BioStar].[dbo].[TB_EVENT_LOG]
)
SELECT * INTO #tempAtten FROM ABC
INSERT [HR].[dbo].[HR_DeviceInOut](DeviceID, CardNo, InOutDate, InOutTime, ShiftprofileID, ExecutedBy)
SELECT DeviceID, CardNo, InOutDate, InOutTime, NULL, NULL
FROM #tempAtten
WHERE #tempAtten.InOutDate = CONVERT(DATE, GETDATE()) AND #tempAtten.CardNo <> 0
DROP TABLE #tempAtten
--HR_DeviceInOut
CREATE TABLE [dbo].[HR_DeviceInOut](
[id] [bigint] IDENTITY(100000000000001,1) NOT NULL,
[DeviceID] [nvarchar](20) NULL,
[CardNo] [nvarchar](20) NOT NULL,
[InOutDate] [date] NOT NULL,
[InOutTime] [nvarchar](10) NOT NULL,
[ShiftprofileID] [tinyint] NULL,
[ExecutedBy] [int] NULL,
CONSTRAINT [PK_HR_AttenHistory] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
--Function
ALTER FUNCTION [dbo].[fn_ConvertToDateTime] (#Datetime BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #LocalTimeOffset BIGINT
,#AdjustedLocalDatetime BIGINT;
SET #LocalTimeOffset = DATEDIFF(second,GETDATE(),GETUTCDATE())
SET #AdjustedLocalDatetime = #Datetime - #LocalTimeOffset
RETURN (SELECT DATEADD(second,#AdjustedLocalDatetime, CAST('1970-01-01 00:00:00' AS datetime)))
END;
Assuming I'm understanding correctly, here's one option using not exists:
INSERT [HR].[dbo].[HR_DeviceInOut] (DeviceID, CardNo, InOutDate,
InOutTime, ShiftprofileID, ExecutedBy)
SELECT DeviceID, CardNo, InOutDate, InOutTime, NULL, NULL
FROM #tempAtten t
WHERE t.InOutDate = CONVERT(DATE, GETDATE()) AND
t.CardNo <> 0 AND
NOT EXISTS (
SELECT 1
FROM [HR].[dbo].[HR_DeviceInOut] d
WHERE t.DeviceID = d.DeviceId AND
t.CardNo = d.CardNo AND
t.InOutDate = d.InOutDate AND
t.InOutTime = d.InOutTime
)
Consider adding a unique_index to the those fields that cannot be duplicated.
Which column set make record unique as i see some column are hard coded
ie 5 AS DeviceID ...
Create unique key for rest of the column in temp table and destinationtabel.to avoid duplicate .

updated record is inserting into the history table not the old record

i have two tables Test and TestHistory
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
INSERT INTO TEST ([ID],[Name],[Status],[CreatedDate])values (1,'Mohan','A',GETDATE())
Created Trigger :
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
Declare #ID INT;
Declare #Name varchar(10);
Declare #Status CHAR(2);
Declare #CreatedDate DATETIME;
Select #ID = I.ID from INSERTED I
Select #Name = I.Name from INSERTED I
Select #Status = I.Status from INSERTED I
Select #CreatedDate = I.CreatedDate from INSERTED I
INSERT INTO [dbo].[Test_history]
([ID]
,[Name]
,[Status]
,[CreatedDate]
)
SELECT #ID,
#Name,
#Status,
GETDATE()
FROM INSERTED I
WHERE #ID = [ID]
When I'm updating the record like
Update [TEST] SET Status = 'I' then the old record with Status = 'A' should inserted but the what ever i'm updating it has been inserting into Testhistory table not the old record
where i'm doing wrong and how to insert old value
like if i updating Status = 'I' and in history table Status = 'A' shoul be inserted
You need to INSERT from DELETED not from INSERTED.
See examples here Understanding SQL Server inserted and deleted tables for DML triggers.
Like Karl mentioned, you need to refer deleted table for old updated row information. Apart from that your existing code doesn't work when you update more than a single row. What you need is something like this.
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
INSERT INTO [dbo].[Test_history]
(
[ID],
[Name],
[Status],
[CreatedDate]
)
SELECT ID,
Name,
Status,
GETDATE()
FROM deleted d

Cursor to fill data in table?

CREATE TABLE [dbo].[Table_A](
[ID] [int] IDENTITY(1,1) NOT NULL,
[AREA] [varchar](50) NOT NULL,
[Year] [int] NOT NULL,
[Month] [int] NOT NULL,
[Factor] [float] NULL,
[Net] [float] NULL,
[Path] [varchar](255) NULL,
[Created] [smalldatetime] NULL,
[CreatedBy] [varchar](50) NOT NULL,
[LastModified] [varchar](50) NOT NULL,
[LastModifiedBy] [varchar](50) NOT NULL)
)
ALTER TABLE [dbo].[Table_A] ADD DEFAULT ((1.0)) FOR [Factor]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (sysdatetime()) FOR [Created]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (suser_name()) FOR [CreatedBy]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (sysdatetime()) FOR [LastModified]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (suser_name()) FOR [LastModifiedBy]
GO
I need to fill up the Table using cursor where
cursor should fetch data from
Declare Cur_AREA Cursor
For
Select Distinct Media From DIM_AREA
Note:DIM_AREA CONSISTS OF texas,dallas,chicago and newark.
Cursor should fill data for years 1990 to 2020
My cursor code is::
Declare #Temp_Year int
Select #Temp_Year = MAX(Year)
While #Temp_Year < DATEPART(YEAR, GETDATE())
Begin
Declare Cur_Media Cursor
For
Select Distinct Media From DIM_AREA
Order by Media
open Cur_Media
Fetch Next From Cur_Media
While ##FETCH_STATUS = 0
Begin
Declare #Temp_Month int
Set #Temp_Month = 1
While #Temp_Month <= 12
Begin
Insert into Table_A (Media, Year, month)
Set #Temp_Month = #Temp_Month + 1
Set #Temp_Year = #Temp_Year + 1
end
end
Close Cur_Media
Deallocate Cur_Media
But my cursor not working properly :(
You may have other problems here, I don't think your question is fleshed out, but you say:
Insert into Table_A (Media, Year, month)
But you don't pick anything to be inserted.
Use:
Insert into Table_A (Media, Year, month)
VALUES('','','')
or:
Insert into Table_A (Media, Year, month)
SELECT '','',''
FROM YourTable
Replacing the empty quotes with whatever you are trying to insert.

How to solve "Self referencing table conflicted with the FOREIGN KEY constraint"

I am creating a simple table with automatically generated employee id with a prefix like
sale_900000,sale_900001,sale_900002,sale_900003
It is a self-referencing table.
When I insert data into the table I get errors like this:
*Msg 547, Level 16, State 0, Procedure tr_generate_emp_id, Line 42
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "Registration_Registration". The conflict occurred in database "test", table "dbo.Registration", column 'empid'.*
For automatic employee id I am using an instead of insert trigger on the table
This is my table
CREATE TABLE [dbo].[Registration](
[empid] [varchar](40) NOT NULL,
[id] [int] IDENTITY(900000,1) NOT NULL,
[First_Name] [varchar](40) NULL,
[Last_Name] [varchar](40) NULL,
[Address] [varchar](40) NULL,
[E_Mail] [varchar](40) NULL,
[Country] [varchar](40) NULL,
[Mobile_No] [varchar](40) NULL,
[Designation] [varchar](40) NULL,
[managerID] [varchar](40) NULL,
CONSTRAINT [PK_Registration] PRIMARY KEY CLUSTERED
(
[empid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Registration] WITH CHECK ADD CONSTRAINT [Registration_Registration] FOREIGN KEY([managerID])
REFERENCES [dbo].[Registration] ([empid])
and this is my trigger
ALTER TRIGGER [dbo].[tr_generate_emp_id] ON [dbo].[Registration]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #id INT
DECLARE #id1 INT
DECLARE #id_s VARCHAR(50)
DECLARE #empid VARCHAR(50)
DECLARE #First_Name VARCHAR(50)
DECLARE #Last_Name VARCHAR(50)
DECLARE #Address VARCHAR(50)
DECLARE #E_Mail VARCHAR(50)
DECLARE #Country VARCHAR(50)
DECLARE #Mobile_No VARCHAR(50)
DECLARE #Designation VARCHAR(50)
DECLARE #managerID VARCHAR(50)
SELECT #id = Id
FROM dbo.Registration
IF #id IS NULL
SET #id = 899999
SET #id1 = #id + 1
SELECt #First_Name = First_Name ,
#Last_Name = Last_Name ,
#Address = Address ,
#E_Mail = E_Mail ,
#Country = Country ,
#Mobile_No = Mobile_No ,
#Designation = Designation ,
#managerID = managerID
FROM INSERTED
SET #id_s = CONVERT(VARCHAR(50), #id1)
SET #empid = 'Sale_' + #id_s
INSERT INTO dbo.Registration
( empid ,
First_Name ,
Last_Name ,
Address ,
E_Mail ,
Country ,
Mobile_No ,
Designation ,
managerID
)
VALUES ( #empid , -- empid - varchar(40)
#First_Name , -- First_Name - varchar(40)
#Last_Name , -- Last_Name - varchar(40)
#Address , -- Address - varchar(40)
#E_Mail , -- E_Mail - varchar(40)
#Country, -- Country - varchar(40)
#Mobile_No , -- Mobile_No - varchar(40)
#Designation , -- Designation - varchar(40)
#managerID -- managerID - varchar(40)
)
END
i am inserting
INSERT INTO dbo.Registration
( empid ,
First_Name ,
Last_Name ,
Address ,
E_Mail ,
Country ,
Mobile_No ,
Designation ,
managerID
)
VALUES ( '' , -- empid - varchar(40)
'admin' , -- First_Name - varchar(40)
'jon' , -- Last_Name - varchar(40)
's-24' , -- Address - varchar(40)
'abc#gmail.com' , -- E_Mail - varchar(40)
'india' , -- Country - varchar(40)
'098735322211' , -- Mobile_No - varchar(40)
'manager' , -- Designation - varchar(40)
'' -- managerID - varchar(40)
)
and error
Msg 547, Level 16, State 0, Procedure tr_generate_emp_id, Line 42
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "Registration_Registration". The conflict occurred in database "test", table "dbo.Registration", column 'empid'.
The statement has been terminated.
The main issue is that you're doing this in the first place. I just read through your trigger. Just changed empid to be a computed field...
CREATE TABLE [dbo].[Registration](
[empid] AS ('Sale_' + Convert(Varchar(50),id)) PERSISTED,
[id] [int] IDENTITY(900000,1) NOT NULL,
[First_Name] [varchar](40) NULL,
[Last_Name] [varchar](40) NULL,
[Address] [varchar](40) NULL,
[E_Mail] [varchar](40) NULL,
[Country] [varchar](40) NULL,
[Mobile_No] [varchar](40) NULL,
[Designation] [varchar](40) NULL,
[managerID] [varchar](40) NULL,
CONSTRAINT [PK_Registration] PRIMARY KEY CLUSTERED
(
[empid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
In your trigger I think this line
SELECT #id = Id
is meant to read
SELECT #id = MAX(Id)
I'm surprised you havent received PK violation errors as a result. The FK violation error can only be due to trying to insert MangerId value that doesn't match an empid value (as far as I can see from the code). So check what managerId value you are supplying.
Check your Nest Level.
http://msdn.microsoft.com/en-us/library/ms182737(v=sql.100).aspx
I would put this value in a (dbo.) AuditTable, something that has nothing to do with your primary table.
trigger_nestlevel()
Change from Row Based to Set Based Logic.
I re-did your trigger to be set based but I think #Love2Learn may have a better solution for you.
ALTER TRIGGER [dbo].[tr_generate_emp_id] ON [dbo].[Registration]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #id INT
-- Get the last id used.
SELECT #id = MAX(Id)
FROM dbo.Registration
IF #id IS NULL
SET #id = 899999
WITH MyInserted AS (SELECT
ROW_NUMBER() OVER(ORDER BY E_Mail) AS rownum-- Email is just a random order by here. Could be anything you want.
,*
FROM inserted)
INSERT INTO dbo.Registration
( empid ,
First_Name ,
Last_Name ,
Address ,
E_Mail ,
Country ,
Mobile_No ,
Designation ,
managerID
)
SELECT -- add the last id used with the rownum (ranges from 1 to num of rows)
'Sale_'+CONVERT(VARCHAR(50), #id+rownum) -- empid - varchar(40)
First_Name, -- First_Name - varchar(40)
Last_Name, -- Last_Name - varchar(40)
Address, -- Address - varchar(40)
E_Mail, -- E_Mail - varchar(40)
Country, -- Country - varchar(40)
Mobile_No, -- Mobile_No - varchar(40)
Designation, -- Designation - varchar(40)
ManagerID -- managerID - varchar(40)
FROM MyInserted
END