trigger problems on sql - sql

need to run a trigger that updated a column in my customers table. the idea is that once the customers table is updated the trigger needs to update a different column with a scalar. the scalar is calculated by a function.
I don't know where is the issue but something is going very wrong.
create FUNCTION calculateVirtualCashForCustomer(#email varchar)
RETURNS float
AS BEGIN
DECLARE #virtualCashToAdd float
select #virtualCashToAdd= (B.Price*(cast(O.Quantity as float)))*0.1
from CUSTOMERS AS C JOIN BOXES AS B ON C.Email = B.Email JOIN ORDERS AS O ON O.BoxID = B.BoxID
where C.Email = #email
RETURN #virtualCashToAdd
End
CREATE TRIGGER updateVirtualCash
on CUSTOMERS
AFTER UPDATE
AS
DECLARE #Mail varchar(50)
begin
update CUSTOMERS
set [Virtual Cash] = [Virtual Cash] + (dbo.calculateVirtualCashForCustomer(#Mail))
where #Mail= (select Email from dbo.Top10byMoney)
end

I think that the issue is in where #Mail= (select Email from dbo.Top10byMoney) of the trigger. you can try add top 1 in select, so to compare only with a selected value
Example:
CREATE TRIGGER updateVirtualCash
on CUSTOMERS
AFTER UPDATE
AS
DECLARE #Mail varchar(50)
SET #Mail= (select Email from dbo.Top10byMoney)
begin
update CUSTOMERS
set [Virtual Cash] = [Virtual Cash] + (dbo.calculateVirtualCashForCustomer(#Mail))
where Email=#Mail
end
for solve the values null problem, you can use ISNULL
set [Virtual Cash] = ISNULL([Virtual Cash],0) + ISNULL(dbo.calculateVirtualCashForCustomer(#Mail),0)

Related

SQL I want to have a piece deducted from the stock, and in the event that a piece is retrieved it is added to the store

I am using this SQL code but it works on the discount only. I want to make it in the event that a positive value is entered in the sales table, it is deducted from the inventory table, and in the case of entering a negative value, it increases in the inventory table
CREATE TRIGGER [dbo].[TryCutStock_By_Insert]
ON [dbo].[SaleDetail]
FOR INSERT
AS
DECLARE #StockQty AS varchar(10)
DECLARE #SaleQty AS varchar(10)
DECLARE #ProID AS varchar(50)
DECLARE #Result AS varchar(50)
SELECT #ProID = i.Code FROM inserted i;
SELECT #StockQty = ItemCard.Qty
FROM ItemCard
WHERE ItemCard.Code = #ProID;
SELECT #SaleQty = i.Qty FROM inserted i;
SELECT
#Result = CONVERT (int, #StockQty) - CONVERT(int, #SaleQty);
BEGIN
UPDATE ItemCard
SET Qty = #Result
WHERE Code = #ProID;
END
PRINT ''
Few things.
Don't use VARCHAR datatype for storing integers. Use right datatype.
As suggested by #Mitch-wheat, design the trigger to be batch aware
Don't have PRINT statement in the trigger
begin
update ic
set Qty= CONVERT (int,ic.Qty) - CONVERT(int,i.Qty)
FROM ItemCard AS ic
INNER JOIN inserted as i
ON i.Code = ic.Code
end

UDF don't give the same result

when I exececute the UDF out of the trigger the result is not the same
the UDF return always true when executing in the trigger
but out of the trigger the result is true or false
ALTER FUNCTION [dbo].[MandatExist]
(
#Numero int,
#IdBranche int,
#Exercice int
)
RETURNS bit
AS
BEGIN
DECLARE #Result bit
DECLARE #Nbr int
DECLARE #Categ int
SELECT #Categ = CategorieNumero
FROM Branche
WHERE IdBranche = #IdBranche
SELECT #Nbr=COUNT(*)
FROM Mandat AS M INNER JOIN Branche AS B ON M.IdBranche=B.IdBranche
WHERE (Numero = #Numero) AND (B.CategorieNumero = #Categ) AND (Exercice = #Exercice)
IF #Nbr = 0
SET #Result = 0
ELSE
SET #Result = 1
RETURN #Result
END
the trigger call MandatExist to get if the number exist or not
ALTER TRIGGER [dbo].[ValidInsertUpdate_Mandat]
ON [dbo].[Mandat]
FOR INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Cloturer AS bit
DECLARE #Exercice AS int
DECLARE #IdBranche AS int
DECLARE #Numero AS int
DECLARE #Message AS nvarchar(100)
SELECT #Cloturer=Cloturer, #Exercice=Exercice, #Numero=Numero, #IdBranche=IdBranche
FROM INSERTED
IF (dbo.MandatExist(#Numero, #IdBranche, #Exercice)=1)
BEGIN
SET #Message = 'Numero de mandat existant.'
RAISERROR(#Message, 16, 1)
ROLLBACK TRAN
END
INSERTED is a table, thus may contain more than one row which means that this code
SELECT #Cloturer=Cloturer, #Exercice=Exercice, #Numero=Numero, #IdBranche=IdBranche
FROM INSERTED
is essentially incorrect.
UDFs are not the best choice for set-based programming and may cause performance degradation. Specifically this UDF makes absolutely no sense, has no reason for encapsulating this code into separate module. This is a trivial EXISTS code.
Entire function can and must be replaced by EXISTS statement, the whole code of the trigger might look like:
IF EXISTS(
SELECT 1
FROM INSERTED
INNER JOIN ...
WHERE ...
)
BEGIN
RAISERROR(...)
END
I'm not sure what's the meaning of your tables and columns, but I assume you are trying to check some uniqueness. So, assuming that you don't want another Mandat record for the same CategorieNumero, the final EXISTS might look like:
IF EXISTS(
SELECT 1
FROM INSERTED i
INNER JOIN Branch b on b.IdBranche = i.IdBranch
-- other branches with the same CategorieNumero; why isn't CategorieNumero unique?
INNER JOIN Branch b_dup on b_dup.CategorieNumero = b.CategorieNumero
-- existing Mandat rows for the same CategorieNumero with any IdBranch
INNER JOIN Mandat m_dup on m_dup = b_dup.IdBranch
-- ensure you're not comparing inserted/updated Mandat row to itself
WHERE i.ID != m_dup.ID
)
...
But your intent is unclear to me and I think that after clarification most of your needs will be easily satisfied by unique constraints.
If you don't want more that one row for each set of (Exercice, Numero, IdBranch) - just add a unique constraint to Mandat table and drop both trigger and function!
Don't overuse triggers. And UDFs.
i have used the solution of Ivan
IF EXISTS(
SELECT 1
FROM INSERTED I INNER JOIN Branche b ON b.IdBranche = i.IdBranche
INNER JOIN Branche b_dup ON b_dup.IdBranche = b.IdBranche
INNER JOIN Mandat m_dup on (m_dup.Exercice = i.Exercice) AND (m_dup.Numero = i.Numero) AND (b_dup.IdBranche=i.IdBranche)
WHERE i.IdMandat != m_dup.IdMandat
)
BEGIN
RAISERROR('error', 16, 1)
ROLLBACK TRAN
END

Update Trigger For Multiple Rows

I am trying to Insert data in a table named "Candidate_Post_Info_Table_ChangeLogs" whenever a record is updated in another table named "Candidate_Personal_Info_Table". my code works fine whenever a single record is updated but when i try to updated multiple rows it gives error:
"Sub query returned more then 1 value".
Following is my code :
ALTER TRIGGER [dbo].[Candidate_PostInfo_UPDATE]
ON [dbo].[Candidate_Post_Info_Table]
AFTER UPDATE
AS
BEGIN
IF ##ROWCOUNT = 0
RETURN
DECLARE #Candidate_Post_ID int
DECLARE #Candidate_ID varchar(50)
DECLARE #Action VARCHAR(50)
DECLARE #OldValue VARCHAR(MAX)
DECLARE #NewValue VARCHAR(MAX)
DECLARE #Admin_id int
IF UPDATE(Verified)
BEGIN
SET #Action = 'Changed Verification Status'
SET #Candidate_Post_ID = (Select ID From inserted)
SET #Candidate_ID = (Select Identity_Number from inserted)
SET #NewValue = (Select Verified From inserted)
SET #OldValue = (Select Verified From deleted)
IF(#NewValue != #OldValue)
BEGIN
INSERT INTO Candidate_Post_Info_Table_ChangeLogs(Candidate_Post_ID, Candidate_ID, Change_DateTime, action, NewValue, OldValue, Admin_ID)
VALUES(#Candidate_Post_ID, #Candidate_ID, GETDATE(), #Action, #NewValue, #OldValue, '1')
END
END
END
i have searched stack overflow for this issue but couldn't get any related answer specific to this scenario.
When you insert/update multiple rows into a table, the Inserted temporary table used by the system holds all of the values from all of the rows that were inserted or updated.
Therefore, if you do an update to 6 rows, the Inserted table will also have 6 rows, and doing something like this:
SET #Candidate_Post_ID = (Select ID From inserted)
Will return an error, just the same as doing this:
SET #Candidate_Post_ID = (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6)
From the looks of things, you tried to do this with an iterative approach. Set-based is better. Maybe consider doing it like this in the body of your TRIGGER (without all of the parameters...):
IF UPDATE(Verified)
BEGIN
INSERT INTO Candidate_Post_Info_Table_ChangeLogs
(
Candidate_Post_ID
,Candidate_ID
,Change_DateTime
,action
,NewValue
,OldValue
,Admin_ID
)
SELECT
I.ID
,I.Identity_Number
,GETDATE()
,'Changed Verification Status'
,I.Verified
,O.Verified
,'1'
FROM Inserted I
INNER JOIN Deleted O
ON I.ID = O.ID -- Check this condition to make sure it's a unique join per row
WHERE I.Verified <> O.Verified
END
A similar case was solved in the following thread using cursors.... please check it
SQL Server A trigger to work on multiple row inserts
Also the below thread gives the solution based on set based approach
SQL Server - Rewrite trigger to avoid cursor based approach
*Both the above threads are from stack overflow...

How to create procedure with multiple string select query in sql?

I want to create procedure with multiple string select query.I want to insert data to table variable and join that temp table with other table.
I don't want to create temp table as real tables. I want to insert data to memory temp table.
Here is my procedure,
CREATE PROCEDURE sp_TempBatch
AS
DECLARE #TempBatchSerial TABLE
(
ID int,
Name nvarchar(200),
StockType nvarchar(50),
ItemNo nvarchar(50)
)
DECLARE #TempQuery as nvarchar(MAX)='',
#VendorQuery as nvarchar(MAX)=''
BEGIN
SET #TempQuery='SELECT ID,Name,'
IF StockType = '1'
BEGIN
SET #TempQuery += ' ''Batch'' as StockType,'
END
ELSE
BEGIN
SET #TempQuery += ' ''Serial'' as StockType,'
END
SET #TempQuery += 'ItemNo INTO #TempBatchSerial
FROM Stock'
EXEC (#TempQuery)
SET #VendorQuery+=' SELECT #TempBatchSerial.* FROM #TempBatchSerial
INNER JOIN Vendor
ON #TempBatchSerial.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey'
EXEC (#VendorQuery)
END
When execute procedure show error message of Must declare the table variable "#TempBatchSerial"
You have to refer to #tempBatchSerial via an Alias
That's the only way #tempTables can be referred or linked to.
SELECT T.* FROM #TempBatchSerial T
INNER JOIN Vendor
ON T.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey
If that doesn't work you can try to put the #tempTable in the #vendorQuery text.

Creating a Stored Procedure

I am trying to create a stored procedure that will allow me to update the ActualArrivalDate and ActualDepartureDate in a TripStop table where StopTypeCode = Drop in the TripStopOrder table.
Create Proc spUpdateTable
#OrderID int, #ArrivalDate datetime, #DepartureDate datetime
As
Begin
Begin Transaction
Select * From dbo.TripStopOrder
Join dbo.TripStop ON dbo.TripStopOrder.TripStopID = dbo.TripStop.TripStopID
Where OrderID = ''and StopTypeCode ='Drop'
Once I find this record I need to grab the TripStopId and pass it into the Update statement.
Not sure how to this...can I use a temp table then run another Select statement to pick up the TripStopID?
Update dbo.TripStop SET ArrivalDate='',DepartureDate=''
Where TripStopID = ''
End
Commit
Any ideas or suggestions would be greatly appreciated. ~Newbie~
You can assign the value to a variable such as #TripStopId:
DECLARE #TripStopId INT
Select #TripStopid = TripStopId
From dbo.TripStopOrder
Join dbo.TripStop ON dbo.TripStopOrder.TripStopID = dbo.TripStop.TripStopID
Where OrderID = ''and StopTypeCode ='Drop'
Then you can use it in your UPDATE statement.
Update dbo.TripStop SET ArrivalDate='',DepartureDate=''
Where TripStopID = #TripStopId
Instead of doing the SELECT and then the UPDATE, just change your WHERE clause in your UPDATE statement:
WHERE TripStopID = (SELECT T.TripStopID FROM TripStopOrder O
INNER JOIN TripStop T ON O.TripStopID = T.TripStopID
WHERE OrderID = #OrderID AND StopTypeCode = 'Drop')