IF statement in SQL Server trigger with condition - database

I created a table student and I want to create a trigger to stop adding
teacher id after 6 to any student so every 5 student have only 1 teacher.
Teacher id in student table is a foreign key so I can repeat in student table
5 times and when I insert 6.. don't agree I try this code but it does not work
The student table looks like this:
CREATE TABLE student
(
s_id int PRIMARY key,
s_name varchar(50) ,
birthday date ,
t_id int
)
And the trigger:
CREATE TRIGGER tr_student
on student
DECLARE
#count int,
#s_id int,
#s_name nvarchar(50),
#birthdate date,
#t_id int
Begin
select #s_id = i.s_id from inserted i;
select #s_name = i.s_name from inserted i;
select #birthdate = i.birthday from inserted i;
select #t_id = i.t_id from inserted i;
Select count(*) t_id
From student
Where #count = t_id
If #count < 6
Insert into student(s_id, s_name, birthday, t_id)
values(#s_id, #s_name, #birthdate, #count)
PRINT 'AFTER INSERT trigger fired'
End

Your fundamental flaw is that you seem to expect the trigger to be fired once per row - this is NOT the case in SQL Server. Instead, the trigger fires once per statement, and the pseudo table Inserted might contain multiple rows.
Given that that table might contain multiple rows - which one do you expect will be selected here??
select #s_id = i.s_id from inserted i;
select #s_name = i.s_name from inserted i;
select #birthdate = i.birthday from inserted i;
select #t_id = i.t_id from inserted i;
It's undefined - you'll get the values from arbitrary rows in Inserted, and you'll ignore all other rows.
You need to rewrite your entire trigger with the knowledge the Inserted WILL contain multiple rows! You need to work with set-based operations - don't expect just a single row in Inserted !
Furthermore: the trigger fires AFTER the insert has already happened, so your code here:
If #count < 6
Insert into student(s_id, s_name, birthday, t_id)
values(#s_id, #s_name, #birthdate, #count)
basically inserts the same data again - certainly not what you want.
So you need to
rewrite your trigger to properly deal with multiple rows in Inserted
either switch to a INSTEAD OF INSERT trigger, or change your trigger logic

Related

Issue with SQL INSERT Trigger

I'm reaching out for some help on this trigger I'm trying to get working.
Basically this is what I'm trying to do.
We have DMS software that writes to a Database and on a particular INSERT value I want the trigger to fire.
This is an example of an INSERT statement that will get processed.
INSERT INTO DOCSADM.ACTIVITYLOG (CR_IN_USE,ACTIVITY_DESC,BILLED_ON,BILLABLE,PAGES,KEYSTROKES,
TYPE_TIME,ELAPSED_TIME,TYPIST,AUTHOR,START_DATE,ACTIVITY_TYPE,REF_DOCUMENT,REF_LIBRARY,APPLICATION,VERSION_LABEL,DOCNUMBER,SYSTEM_ID)
VALUES ('','DOCSFusion','1753-01-01','',0,0,0,0,1920,1920,'2020-08-26T10:17:56',**115**,0,-1,1173,'',75,3252)
but I only want the trigger to fire when we see a value of 115 for the bold section in the INSERT statement (the Activity_type value).
For all other values that re not 115 I don't want to do anything.
This is what I have so far:
CREATE TRIGGER BW_TRIGGER
ON DOCSADM.ACTIVITYLOG
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--Declare some variable and set it as a value of 115.
--Example:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM DOCSADM.ACTIVITYLOG A, INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from docsadm.ACTIVITYLOG A, INSERTED I)
--Next, you should have a fork or path in your trigger to determine how it proceeds.
--Path 1: The #AlogType value matches the inserted value so you want to process the rest of the trigger. Example path – “ProcessTrigger:”
--Path 2: The #AlogType value does NOT match the inserted value, you want to exit the trigger. Example Path – “ExitTrigger:”
IF #AlogType <> 115
GOTO TriggerExit;
ELSE
Begin
/*Create first temp table to collect insert values*/ --This table will have the SysID Value and the corresponding docnumber for the items you want.
--You can add whatever other values you think you need.
CREATE TABLE #TempSet1
(
AlogsysID INT,
Docnum INT,
AlogDate Varchar(64),
AlogTypist INT,
AlogAuthor INT,
AlogDesc varchar(32),
ALOGVER varchar(10),
ALOG_MATTER INT
)
INSERT INTO #TempSet1 (AlogsysID,Docnum,AlogDate,AlogTypist,AlogAuthor, ALOG_MATTER)
--SELECT  You SELECT STATEMENT WILL GO HERE MODIFIED TO POPULATE THE TABLE WITH THE DOCNUMBERS YOU WANT!!
select top 1 System_id, docnumber, LAST_ACCESS_DATE, TYPIST, AUTHOR, MATTER from docsadm.PROFILE where EXISTS (SELECT CLIENT.SYSTEM_ID FROM DOCSADM.CLIENT INNER JOIN DOCSADM.MATTER ON MATTER.CLIENT_ID = CLIENT.SYSTEM_ID
WHERE MATTER.SYSTEM_ID =#AlogDesc OR INH_LUP_SEC_FROM IS NULL OR INH_LUP_SEC_FROM = 0) AND MATTER=#AlogDesc
/*Set variable #SysID as the LASTKEY value -1. This will be used to set the SysID column on the #TempSet table*/
--DECLARE #SysID INT = (SELECT LASTKEY FROM DOCSADM.SEQ_SYSTEMKEY) -1;
/*Set the SysID value for every row on the #TempSet1 table as the #SysID variable +1*/
--UPDATE #TempSet1
--SET #SysID = AlogsysID = #SysID + 1
--Your #TempSet should now be set with ALL of the System_IDs and Docnumbers necessary for your insert!!!!—
--Verify this by doing a select against the #TempSet1 Table
SELECT * FROM #TempSet1;
--Next you need to set the SystemID to the correct value for future processing. To do this, we need to get a total count from the #TempSet table.
/*Set a variable to update the NEXTKEY value on the DOCSADM.SEQ_SYSTEMKEY table. The NEXTKEY value is used for the SYSTEM_ID field*/
--DECLARE #SeqUpdateCount INT = (SELECT COUNT(*) FROM #TempSet1);
/*Update the LASTKEY Value on the SEQ_SYSTEMKEY table to the next available value for DM.*/
--UPDATE DOCSADM.SEQ_SYSTEMKEY SET LASTKEY = LASTKEY+#SeqUpdateCount
--If you have all the values you need in your temp table, you can now insert them into the ACTIVITYLOG table.
--INSERT INTO DOCSADM.ACTIVITY
--(SYSTEM_ID, DOCNUMBER, START_DATE, version, EXT,)
--SELECT
--AlogSysID,Docnum,GETUTCDATE(),BLAH, BLAH
--FROM #TableSet1
INSERT INTO DOCSADM.ACTIVITYLOG
(SYSTEM_ID,
DOCNUMBER,
START_DATE,
TYPIST,
AUTHOR,
ACTIVITY_DESC,
VERSION_LABEL,
ACTIVITY_TYPE)
SELECT
AlogsysID, Docnum,AlogDate,AlogTypist, AlogAuthor, ALOG_MATTER, '',115
FROM #TempSet1;
--Now you need to Drop the Temp Table
DROP TABLE #TempSet1
--Go to the other half of your path above to exit the trigger.
END
TriggerExit:
END
Go
but when I try to run any INSERT statement on this table I get this error message. It doesn't matter if the activity_type has a value of 115 or not
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I know the issue is with this section of the trigger:
INSERT INTO #TempSet1 (AlogsysID,Docnum,AlogDate,AlogTypist,AlogAuthor, ALOG_MATTER)
--SELECT  You SELECT STATEMENT WILL GO HERE MODIFIED TO POPULATE THE TABLE WITH THE DOCNUMBERS YOU WANT!!
SELECT TOP 1
System_id
, docnumber
, LAST_ACCESS_DATE
, TYPIST
, AUTHOR
, MATTER
FROM docsadm.PROFILE
WHERE EXISTS (SELECT CLIENT.SYSTEM_ID
FROM DOCSADM.CLIENT
INNER JOIN DOCSADM.MATTER
ON MATTER.CLIENT_ID = CLIENT.SYSTEM_ID
WHERE MATTER.SYSTEM_ID =#AlogDesc
OR INH_LUP_SEC_FROM IS NULL
OR INH_LUP_SEC_FROM = 0)
AND MATTER=#AlogDesc
It's the SELECT statement that is causing it to fail.
I know that this statement will bring back multiple rows but I only need the value from one of them so I can use this value for my INSERT. I though having the "select top 1" would do this for me but it's not working like I think it should. What am I missing?
If I had to guess I would say your problem is here:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM DOCSADM.ACTIVITYLOG A, INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from docsadm.ACTIVITYLOG A, INSERTED I)
How are ACTIVITYLOG and INSERTED joined in the above ? without a where it would be a CROSS JOIN. Why do you even drag ACTIVITYLOG into it, you can simply use INSERTED. Also please try to stop using implicit joins ( I can see that later down the script you use the proper, more verbose join syntax)
TRY:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from INSERTED I)
Be careful that this will work with single inserts only. When you do batched inserts the INSERTED is a table containing multiple rows and you will run into issues again.

SQL Server stored procedure needs some work

I have two tables Product and Purchase:
Product table:
ProductID = 1
PName = tv
StockQty = 10
Purchase table:
PurchaseID = 1
PID = 1
PurchaseQty = 5
Product ID is a foreign key in Purchase table. I am trying to write a stored procedure so that whenever Purchase Quantity is added in Purchase table, the main table Product should also be changed.
For example: I add purchase quantity 5. When I refresh the Product table, it should display 15. With my current stored procedure, it is updating all products and increases StockQty by 5 - instead it should do this to only the product id I chose. Please advise.
Below is my stored procedure:
CREATE PROCEDURE insertpurchase
#PID int,
#PurchaseQty int,
#StockQty int
AS
INSERT INTO [dbo].[Purchase] (PID, PurchaseQty)
VALUES (#PID, #PurchaseQty)
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + Purchase.PurchaseQty
FROM Purchase
END
Can somebody guide what is wrong with my query?
You need to add where clause while updating.
'CREATE PROCEDURE
insertpurchase
#PID int ,
#PurchaseQty int,
#StockQty int
AS
INSERT INTO [dbo].[Purchase] (PID ,PurchaseQty ) VALUES (#PID ,#PurchaseQty )
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + #PurchaseQty
WHERE ProductID = #PID
END '
It’s true that the stored procedure needs the filter for the ProductID on the update statement. however, to make sure both update and insert are executed properly, I made some adjustment to the code with the following
1.) I removed #StockQty because in that scenario it seems that the stored procedure only require the id of puchases item and purchase quantity
2.) I add the Transaction to make sure that both insert and update are executed properly, without it, it is possible to insert but does not update.
CREATE PROCEDURE insertpurchase
#PID int,
#PurchaseQty int
AS
BEGIN TRANSACTION
INSERT INTO [dbo].[Purchase] (PID, PurchaseQty)
VALUES (#PID, #PurchaseQty)
BEGIN
UPDATE [dbo].[Product]
SET Product.StockQty = Product.StockQty + #PurchaseQty
FROM Purchase
WHERE ProductID = #PID
COMMIT TRANSACTION
END

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).

INSERT and UPDATE after one row inserted MS SQL

I don't know if I'm thinking correct, so I'm open for suggestions.
I'm using MS SQL SERVER 2008 R2.
Here is the 'story'
Everytime someone insert a row into tblDelivered there is a trigger that insert in tblConditionDel five values(1,1,1,1,1). This is a table with an auto ID increment. And for that inserted row must fieldname ConditionID be updated with the ID from tblConditionDel.
I think there is something wrong with my where statement
If I delete the where statement the ID is update for the entire table, but it must be for the one inserted row.
My code:
ALTER TRIGGER [dbo].[trgCond2] ON [dbo].[tblDelivered]
AFTER INSERT
AS
insert into tblConditionDel(Con1,Con2,Con3,Con4, Con5)
values(1,1,1,1,1);
update tblDelivered set ConditionID = (select max(ConditionID) from tblConditionDel)
where (select 1 from inserted) = tblDelivered.ConditionID
Thx in advance
Your solution does not work if more than one row is inserted.
Do like this
CREATE TABLE tblDelivered (
DeliveredID int NOT NULL
,ConditionID int
);
CREATE TABLE tblConditionDel (
ConditionID int IDENTITY(1,1)
,Con1 int NOT NULL
,Con2 int NOT NULL
,Con3 int NOT NULL
,Con4 int NOT NULL
,Con5 int NOT NULL
);
CREATE TRIGGER [dbo].[trgCond2] ON [dbo].[tblDelivered]
AFTER INSERT
AS
SET NOCOUNT ON;
DECLARE #ConditionIDs AS table ( -- stores inserted conditionsIDs
DeliveredID int NOT NULL
,ConditionID int NOT NULL
);
MERGE INTO tblConditionDel -- INSERT does not support OUTPUT INTO for multiple rows
USING inserted AS triggerinserted
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (Con1, Con2, Con3, Con4, Con5)
VALUES (1,1,1,1,1)
OUTPUT triggerinserted.DeliveredID
,inserted.ConditionID
INTO #ConditionIDs;
UPDATE tblDelivered
SET ConditionID = ConditionIDs.ConditionID
FROM tblDelivered
INNER JOIN #ConditionIDs AS ConditionIDs
ON ConditionIDs.DeliveredID = tblDelivered.DeliveredID
-- Test code
INSERT INTO tblDelivered (DeliveredID)
VALUES (4),(5),(6);
SELECT * FROM tblConditionDel
SELECT * FROM tblDelivered
You would want to do something like this:
ALTER TRIGGER [dbo].[trgCond2] ON [dbo].[tblDelivered]
AFTER INSERT
AS
declare #new_id int, #delivered_id int
set #delivered_id = (select ConditionID from inserted) -- captures id from inserted table
insert into tblConditionDel(Con1,Con2,Con3,Con4, Con5)
values(1,1,1,1,1);
set #new_id = scope_identity() -- captures new id from tblCondition into a variable
update tblDelivered set ConditionID = #new_id -- set new tblcondition id
where ConditionID = #delivered_id -- for the record that matches the inserted one

Update the same table in the trigger

I have a table which calculate the total deduction and the remaining deduction from the previous month.
And any update in the remaining deduction of old month should affect the next months in order, so I've created an AFTER UPDATE trigger for this table, but I've noticed that the trigger is not invoked by the UPDATE for the next month.
Check the trigger code
CREATE TRIGGER [dbo].[tgUpdateRemainingBalance]
ON [dbo].[GAT_MONTHLY_DEDUCTION]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #OldTotalDeduction INT
DECLARE #NewTotalDeduction INT
DECLARE #OldRemainingToNextMonth INT
DECLARE #NewRemainingToNextMonth INT
SELECT #OldRemainingToNextMonth = RemainingToNextMonth,
#OldTotalDeduction = TotalDeduction
FROM DELETED
SELECT #NewRemainingToNextMonth = RemainingToNextMonth,
#NewTotalDeduction = TotalDeduction
FROM INSERTED
DECLARE #EmpNo BIGINT
DECLARE #RegDate DATETIME
SELECT #EmpNo = PersonnelNo, #RegDate = RegDate FROM INSERTED
IF (#OldRemainingToNextMonth <> #NewRemainingToNextMonth) OR (#OldTotalDeduction <> #NewTotalDeduction)
BEGIN
UPDATE GAT_MONTHLY_DEDUCTION
SET TotalDeduction = TotalDeduction - #OldRemainingToNextMonth + #NewRemainingToNextMonth,
Visited = 1
WHERE RegDate = DATEADD(month, 1, #RegDate) AND PersonnelNo = #EmpNo
END
END
I am doing updating for the same table which has this triggers but calling the trigger for the first time should call the trigger again for the next update but it is not happening.
It is urgent for me to know how to solve it.
Thanks
One thing to watch out for: the SQL Server trigger is fired once per statement (and NOT once per row as many developer think/assume)
If that statement updates more than one row, then the Inserted and Deleted pseudo tables will contain multiple rows - so your selects like
SELECT
#NewRemainingToNextMonth = RemainingToNextMonth,
#NewTotalDeduction = TotalDeduction
FROM INSERTED
will fail - which row should be selected, if there are fifty of them in Inserted??
You will need to rewrite your trigger to be able to deal with multiple rows being handled at once, in the Inserted and Deleted pseudo tables...

Resources