How do you create a logon trigger in SQL Server 2012 - sql-server

I've never used Triggers before, but need to use one to capture a location based on the IPAddress on login. I'm unsure of the structure and have followed guidance that was forwarded to me, but I cannot get it to work. This is what I have at the moment -
CREATE TRIGGER LGNCC_TRIGLOG
ON LGNCC_LOGIN
AFTER INSERT
AS
declare #userid VARCHAR(10)
declare #ipaddress VARCHAR(100)
declare #lastactivity VARCHAR(35)
BEGIN
select
#userid = I.userid,
#ipaddress = I.ipaddress,
#lastactivity = I.lastactivity
from
LGNCC_LOGIN I
IF (#lastactivity = 'login')
BEGIN
INSERT INTO lgnco_agent_location2(user_id, user_ip, user_location, user_date, User_Time)
SELECT
#userid, #ipaddress, 'This is a test' as Location,
LEFT (CAST (GETDATE() AS DATE),10) AS USERDATEIN,
LEFT (CAST (GETDATE() AS TIME), 8) AS USERTIMEIN
END
END
I've tried a number of different ways of compiling this, but nothing seems to work. Any help would be appreciated.

You trigger is executed after some data is inserted in the table LGNCC_TRIGLOG.
The insert is probably done from an application.
In that case the trigger can done as
CREATE TRIGGER LGNCC_TRIGLOG
ON LGNCC_LOGIN
AFTER INSERT
AS
BEGIN
INSERT INTO lgnco_agent_location2([user_id], user_ip, user_location
, user_date, User_Time)
SELECT userid, ipaddress, 'This is a test' as Location
, LEFT (CAST (GETDATE() AS DATE), 10) AS USERDATEIN
, LEFT (CAST (GETDATE() AS TIME), 8) AS USERTIMEIN
FROM Inserted
WHERE lastactivity = 'login'
END
Inserted is a special table, it is a copy of the trigger table that contains only the rows affected by the last INSERT or UPDATE executed.
If the column User_Date and User_Time in the table lgnco_agent_location2 are of type Date and Time remove the LEFT, as is it useless.

Related

Insert and update multiple records via same stored procedure

I created this stored procedure to go through all the records in the table comparing the id (primary key) if exists and the records changed, make the necessary changes & update the record.
If the id is not in the table then insert the record. This stored procedure
compiles fine, but doesn't seem to work properly. Does this need a while loop?
ALTER PROCEDURE [dbo].[SMLineUpdate]
(
#id [int],
#Payroll_Id [int],
#ProductCode nvarchar(255),
#Description nvarchar (255),
#Qty nvarchar(255)
)
AS
IF EXISTS (SELECT Id from Smline where #id = Id) BEGIN
update dbo.SmLine
Set [Payroll_Id] = #Payroll_Id
, ProductCode = #ProductCode
, Description = #Description
, Qty = #Qty
END ELSE BEGIN
INSERT INTO SmLine ([Payroll_Id], [ProductCode], [Description], [Qty])
VALUES (#Payroll_Id, #ProductCode, #Description, #Qty)
END
Your update query is missing a where condition
update dbo.SmLine
Set [Payroll_Id] = #Payroll_Id
,ProductCode = #ProductCode
,Description = #Description
,Qty = #Qty
WHERE Id = #Id -- the query missed this where condition
IF EXISTS(SELECT Id from Smline where Id =#id)
BEGIN
update dbo.SmLine
Set [Payroll_Id]= #Payroll_Id
,ProductCode= #ProductCode
,Description = #Description
,Qty = #Qty
WHERE Id = #Id
END
ELSE
BEGIN
INSERT INTO SmLine ([Payroll_Id],[ProductCode],[Description],[Qty])
VALUES (#Payroll_Id,#ProductCode ,#Description,#Qty)
END
Your SP does not meet the requirement of insert multiple records. It works only for a single record update or inserts, you have to pass multiple id's and values respectively for update multiple so use a different approach like XML as an input parameter so u can simply do this operation for multiple by extracting the XML data.
Your update statement lacks a where statement. That is a major 'no-no', as it will (god forbid...) update all lines in the table.
Your insert statement lacks an identity insert, so consider the case where you are trying to update/insert id=5, but by now this line is deleted (not found in the where), and ids are much bigger. you would search for it -- > not find, and insert a new line (say id=101), then look for id=5 again, not find it again, and insert it again (say id=102), and so on... I don't think that's what you intended. Consider a Merge statement (when matched/when not matched) and get the best of both worlds. Also consider not deleting from the table, and instead add an 'IsDeleted' column (which allows 'reviving' a deleted row).

i want to limit the data inserting into database table on give date and time only 12

create procedure SP_insert_test #name varchar(20), #emailid varchar(20), #trainer_name varchar(50), #training_date varchar(50), #training_time varchar(50), #gymname varchar(50) , #success int out as
begin
if(
select
count(id)
from
Add_Booking_Fitness_Training
where
training_time = #training_time) > 11 print N'Number of Booking Is Complete for this time and date plz book other time';
else
insert into
Add_Booking_Fitness_Training(memeber_name, member_emailid, trainer_name, training_date, training_time, gymname)
values
(
#name,
#emailid,
#trainer_name,
#training_date,
#training_time,
#gymname
)
SELECT
SCOPE_IDENTITY()
set
#success = 1;
end
begin
set
#success = 0;
end
i have an table in which i want to insert data on give time only 12 member can insert at that time after that they get message list is full plz change the time for inserting i have create procedure its working when its reach number of 12 than its show me message but when i change the time its also show me the same message and not insert any data into database
like 26/04/2018,'6:00' i want to insert this value only 12 time after 12 this show me a message about the limit of number is reach plz change (time)
Create table Add_Booking_Fitness_Training ( id int identity primary key,
memeber_name varchar(20),
member_emailid varchar(20),
trainer_name varchar(50),
training_date varchar(50),
training_time varchar(50),
gymname varchar(50))
i just want to inserting a value into this table only 12 time for a give time like (6:00) if the number of inserting value reach to 12 than its show me the message number of values insert is reach to 12 please change the time.
i want input the value into table only 12 time for a give time 6:00Am when the value is insert into table 12 time than message come up for change time than insert value for change time
Honestly, I am completely guessing here, I still don't really know what you're asking.
I think the OP's statement of "i want input the value into table only 12 time for a give time 6:00Am when the value is insert into table 12 time than message come up for change time than insert value for change time." means that they only want a time to appear in the table up to 12 times. If it appears more than that, the INSERT fails.
This can be achieved with a check constraint and a scalar function. So, as a very simple example:
USE Sandbox;
GO
--Create a very simple table
CREATE TABLE SampleTable (TrainingTime datetime2(0));
GO
--Create the scalar function
CREATE FUNCTION TrainingAtTime (#TrainingTime datetime2(0))
RETURNS INT
AS BEGIN
DECLARE #Trainees int;
SELECT #Trainees = COUNT(*)
FROM SampleTable
WHERE TrainingTime = #TrainingTime;
RETURN #Trainees;
END
GO
--Add the check constraint
ALTER TABLE SampleTable ADD CONSTRAINT MaxTrainees CHECK (dbo.TrainingAtTime(TrainingTime) <= 12) ;
GO
--Insert first trainee
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00');
--It works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--insert 11 more
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00'),
('2018-04-26T06:00:00');
--It works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Try to insert another
INSERT INTO SampleTable
VALUES ('2018-04-26T06:00:00');
--It fails
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Use a different time
INSERT INTO SampleTable
VALUES ('2018-04-26T08:00:00');
--it works
SELECT TrainingTime, COUNT(*) AS Trainees
FROM SampleTable
GROUP BY TrainingTime;
GO
--Clean up
DROP TABLE SampleTable;
DROP FUNCTION TrainingAtTime;
GO
If this isn't what you're after, unfortunately I don't understand your requirements due the the language barrier (and absence of a question).

Field level audit on SQL Server using Trigger

I am trying to write a trigger which would audit a table's every field - a row's old value and new value in a table. If any of the field has been modified, I need to save the fields old value and the new value along with field name in an audit table, as a new entry.
create trigger Trg_Institution_FieldAudit on Table1 AFTER UPDATE AS
DECLARE #OldName VARCHAR(30)
DECLARE #CurrentName VARCHAR(30)
DECLARE #OldId VARCHAR(30)
DECLARE #CurrentId VARCHAR(30)
DECLARE #modifiedBy VARCHAR(30)
If update(Name)
BEGIN
select #OldName = Name from deleted
select #CurrentName = Name from Inserted
select #OldId = ID from deleted
select #currentId = ID from Inserted
select #modifiedBy = modifiedBy from deleted
--INSERT statement for Name field alone
END;
This works fine for a small number of fields, but I have a lot of fields (more than 60), and I am not achieving the performance that is required, because of a lot of if conditions. Is there a better way of doing this? On top of this, there are concurrent updates that are happening to around 3 million records in this table, which makes a lot of things go wrong :(
EDIT: Only ONE row will get updated by an UPDATE statement
Oh my. Please avoid using a cursor whenever possible! You can easily use an insert statement with a select referencing the inserted and deleted tables. Below is a sample from one of my update triggers.
DECLARE #AuditTime DATETIME
SET #AuditTime = GetDate()
IF UPDATE([AccountManager])
INSERT INTO Audit.AuditHistory (AuditId, AuditDate, AuditTableName, EntityKey, AuditFieldName, OldValue, NewValue, FieldDisplayText, OldDisplayText, NewDisplayText, ModifiedBy)
SELECT NewId(),
#AuditTime,
'[tblOpportunity]',
cast(d.[GOTSID] AS varchar),
'[AccountManager]',
cast(d.[AccountManager] AS varchar(250)),
cast(i.[AccountManager] AS varchar(250)),
'Account Manager',
isnull(cast(d.[AccountManager] AS varchar(250)), ''),
isnull(cast(i.[AccountManager] AS varchar(250)), ''),
isnull(i.[ModifiedBy], '')
FROM deleted d
INNER JOIN inserted i ON d.GOTSID = i.GOTSID
WHERE d.[AccountManager] <> i.[AccountManager]
OR (d.[AccountManager] IS NOT NULL
AND i.AccountManager IS NULL)
OR (d.[AccountManager] IS NULL
AND i.AccountManager IS NOT NULL)
#marc_s is right, you have to re-construct your trigger and tables. here take example.
you need to put where condition in select #OldName = Name from deleted.
e.g.-
**
CREATE TRIGGER Trg_Institution_FieldAudit ON Table1 FOR UPDATE
AS
DECLARE #OldName VARCHAR(30)
DECLARE #CurrentName VARCHAR(30)
IF UPDATE (Name)
BEGIN
SET #OldName = Table1.Name FROM deleted
WHERE Table1.Name = deleted.Name;
SET #CurrentName = Table1.Name FROM inserted
WHERE Table1.Name = inserted.Name ;
--INSERT statement for old and new values.
END
GO**
After looking for an alternative for FOR EACH in SQL Server, I found that a CURSOR can be used. It serves the purpose, but need somebody to validate this.
CREATE TRIGGER Trg_Institution_FieldAudit_1 ON dbo.Institution FOR UPDATE as
-- DECLARE Variables
DECLARE institution_cursor CURSOR DYNAMIC FOR SELECT * FROM DELETED
OPEN institution_cursor FETCH NEXT FROM institution_cursor INTO -- #variables here
WHILE (##FETCH_STATUS = 0)
BEGIN
IF UPDATE(COL1)
BEGIN
INSERT INTO AuditTable VALUES (COL1, #prev, #next);
END;
FETCH NEXT FROM institution_cursor INTO -- #Variables here
END
CLOSE institution_cursor
DEALLOCATE institution_cursor

Can't insert a second row into a table though it insert first row by stored procedure

It inserted a first row successfully but it's not inserting any other row, though second row has no conflict of primary key
Code in my aspx.cs file:
outputParVal = sqlCmd.Parameters[outputParName].Value;
outparameter in stored procedure is--- "Result"
CREATE PROCEDURE [dbo].[RecruiterProfileInsert]
#CompanyId int,
#CompanyName varchar(200),
#EmailId varchar(50) ,
#Password varchar(20) ,
#ContactNumber varchar(15),
#Website varchar(50),
#CompanyProfile varchar(2000),
#IsVerified bit,
#Result Tinyint OutPut
--#CreatedDate datetime ,
--UpdatedDate datetime
AS
BEGIN
-- Insert statements for procedure here
--check whether #CompanyName already exist or not if exist then return
IF EXISTS(SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId)))
BEGIN
SET #Result = 0-- Already Exists
END
ELSE
BEGIN
INSERT INTO RecruiterProfile
(
CompanyId,
CompanyName,
EmailId ,
Password ,
ContactNumber,
Website ,
CompanyProfile ,
IsVerified,
CreatedDate
)
VALUES
(
#CompanyId,
#CompanyName,
#EmailId ,
#Password,
#ContactNumber,
#Website,
#CompanyProfile,
#IsVerified,
GetDate()
)
set #Result =1
return
END
END
This is the problem:
SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId))
This inherently makes no sense. You're comparing the variable to itself. Take the # sign out of one of the CompanyId references. The RTrim is unnecessary in SQL Server, and the LTrim doesn't make sense either because the later insert doesn't also LTrim so something is going to go wrong eventually.
Furthermore, inside of an EXISTS clause, TOP makes no sense unless you are using ORDER BY and doing something with the final result. Just do SELECT * inside of EXISTS clauses.
One more thing: if there is high concurrency and users could possibly try to insert the same thing at the same time, your query could still fail on a duplicate key violation.

TSQL Trigger Not Saving Variables and/or not Executing Properly

I've having trouble getting a TSQL trigger to even work correctly. I've run it through the debugger and it's not setting any of the variables according to SQL Server Management Studio. The damnedest thing is that the trigger itself is executing correctly and there are no errors when it is executed (just says 'execution successful').
The code is as follows (it's a work in progress.... just getting my self familiar):
USE TestDb
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'OfficeSalesQuotaUpdate' AND type = 'TR')
DROP TRIGGER OfficeSalesQuotaUpdate
GO
CREATE TRIGGER OfficeSalesQuotaUpdate
ON SalesReps
AFTER UPDATE, DELETE, INSERT
AS
DECLARE #sales_difference int, #quota_difference int
DECLARE #sales_original int, #quota_original int
DECLARE #sales_new int, #quota_new int
DECLARE #officeid int
DECLARE #salesrepid int
--UPDATE(Sales) returns true for INSERT and UPDATE.
--Not for DELETE though.
IF ((SELECT COUNT(*) FROM inserted) = 0)
SET #salesrepid = (SELECT SalesRep FROM deleted)
ELSE
SET #salesrepid = (SELECT SalesRep FROM inserted)
--If you address the #salesrepid variable, it does not work. Doesn't even
--print out the 'this should work line.
PRINT 'This should work...' --+ convert(char(30), #salesrepid)
IF (#salesrepid = NULL)
PRINT 'SalesRepId is null'
ELSE
PRINT 'SalesRepId is not null'
PRINT convert(char(50), #salesrepid)
SET #officeid = (SELECT RepOffice
FROM SalesReps
WHERE SalesRep = #salesrepid)
SELECT #sales_original = (SELECT Sales FROM deleted)
SELECT #sales_new = (SELECT Sales FROM inserted)
--Sales can not be null, so we'll remove this later.
--Use this as a template for quota though, since that can be null.
IF (#sales_new = null)
BEGIN
SET #sales_new = 0
END
IF (#sales_original = 0)
BEGIN
SET #sales_original = 0
END
SET #sales_difference = #sales_new - #sales_original
UPDATE Offices
SET Sales = Sales + #sales_difference
WHERE Offices.Office = #officeid
GO
So, any tips? I've completely stumped on this one. Thanks in advance.
Your main problem seems to be that there is a difference between #foo = NULL and #foo IS NULL:
declare #i int
set #i = null -- redundant, but explicit
if #i = null print 'equals'
if #i is null print 'is'
The 'This should work' PRINT statement doesn't work because concatenating a NULL with a string gives a NULL, and PRINT NULL doesn't print anything.
As for actually setting the value of #salerepid, it seems most likely that the inserted and/or deleted table is in fact empty. What statements are you using to test the trigger? And have you printed out the COUNT(*) value?
You should also consider (if you haven't already) what happens if someone changes more than one row at once. Your current code assumes that only one row is changed at a time, which may be a reasonable assumption in your environment, but it can easily break if someone bulk loads data or does other 'batch processing'.
Finally, you should always mention your MSSQL version and edition; it can be relevant for some syntax questions.
You should replace the body of the trigger with something like this:
;WITH Totals AS (
SELECT RepOffice,SUM(Sales) as Sales FROM inserted GROUP BY RepOffice
UNION ALL
SELECT RepOffice,-SUM(Sales) FROM deleted GROUP BY RepOffice
), SalesDelta AS (
SELECT RepOffice,SUM(Sales) as Delta FROM Totals GROUP BY RepOffice
)
UPDATE o
SET Sales = Sales + sd.Delta
FROM
Offices o
inner join
SalesDelta sd
on
o.Office = sd.RepOffice
This will adequately cope with multiple rows in inserted and deleted. I'm assuming SalesRep is the primary key of the SalesReps table.
Updated above, to cope with UPDATE changing the RepOffice of a particular Sales Rep (which the original doesn't, presumable, get correct either)
Just a suggestion...have you tried putting BEGIN and END to encapsulate the 'AS' part of your trigger?

Resources