i have two tables
PaymentData
Ser Customerid Totalpaid
1. AGP001 2400
2. AGP002 1000
3. AGP003 1500
Receipt
Receipt# Customerid Paid
1. AGP001 1200
2. AGP001 1200
I want to create a trigger on Receipt table, and trigger will fire on insert, update, and delete operations which updates the totalpaid field of PaymentData table. Everytime a new Receipt record is inserted or updated against some customerid, totalpaid field for that customer will updated as well.
Trigger should do following.
Update PaymentData.totalpaid = sum(Recipt.paid)
where Receipt.customerID = PaymentData.customerID
I guess you need some trigger like:
CREATE TRIGGER dbo.OnReceiptUpdate
ON dbo.Receipt
AFTER INSERT,DELETE,UPDATE --operations you want trigger to fire on
AS
BEGIN
SET NOCOUNT ON;
DECLARE #customer_id VARCHAR(10)
SET #customer_id= COALESCE
(
(SELECT customer_id FROM inserted), --table inserted contains inserted rows (or new updated rows)
(SELECT customer_id FROM deleted) --table deleted contains deleted rows
)
DECLARE #total_paid DECIMAL
SET #total_paid =
(
SELECT SUM(paid)
FROM Receipt
WHERE customer_id = #customer_id
)
UPDATE PaymentData
SET total_paid = #total_paid
WHERE customer_id = #customer_id
IF ##ROWCOUNT = 0 --if nothing was updated - you don't have record in PaymentData, so make it
INSERT INTO PaymentData (customer_id, total_paid)
VALUES (#customer_id, #total_paid)
END
GO
Keep in mind - it aint gonna work with multiply updates/deletes/inserts - This is just example of how you need to do it
Try this trigger with multiply updates, inserts or deletes.
CREATE TRIGGER [dbo].upd_PaymentData ON dbo.Receipt
FOR INSERT, UPDATE, DELETE
AS
IF ##ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE #actionTable nvarchar( 10),
#insCount int = (SELECT COUNT(*) FROM inserted),
#delCount int = (SELECT COUNT(*) FROM deleted)
SELECT #actionTable = CASE WHEN #insCount > #delCount THEN 'inserted'
WHEN #insCount < #delCount THEN 'deleted' ELSE 'updated' END
IF #actionTable IN ('inserted', 'updated')
BEGIN
;WITH cte AS
(
SELECT r.Customerid, SUM(r.Paid) AS NewTotalPaid
FROM dbo.Receipt r
WHERE r.Customerid IN (SELECT i.Customerid FROM inserted i)
GROUP BY r.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
ELSE
BEGIN
;WITH cte AS
(
SELECT d.Customerid, SUM(ISNULL(r.Paid, 0)) AS NewTotalPaid
FROM deleted d LEFT JOIN dbo.Receipt r ON d.Customerid = r.Customerid
GROUP BY d.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
CREATE TRIGGER [dbo].upd_PaymentData ON dbo.Receipt
FOR INSERT, UPDATE, DELETE
AS
IF ##ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE #actionTable nvarchar( 10),
#insCount int = (SELECT COUNT(*) FROM inserted),
#delCount int = (SELECT COUNT(*) FROM deleted)
SELECT #actionTable = CASE WHEN #insCount > #delCount THEN 'inserted'
WHEN #insCount < #delCount THEN 'deleted' ELSE 'updated' END
IF #actionTable IN ('inserted', 'updated')
BEGIN
;WITH cte AS
(
SELECT r.Customerid, SUM(r.Paid) AS NewTotalPaid,<strike> r.paymentDate</strike>
FROM dbo.Receipt r
WHERE r.Customerid IN (SELECT i.Customerid FROM inserted i)
GROUP BY r.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
<strike>SET p.lastpaymentDate = c.paymentDate</strike>
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
ELSE
BEGIN
;WITH cte AS
(
SELECT d.Customerid, SUM(ISNULL(r.Paid, 0)) AS NewTotalPaid
FROM deleted d LEFT JOIN dbo.Receipt r ON d.Customerid = r.Customerid
GROUP BY d.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
I tried to add more functionality as indicated inside tage but it gives error and is not working.
Simply added one field in both tables.
In paymentdata added lastpaymentdate,
And in receipt added paymentdate.
On inserting or updating receipt table, paymentdata.lastpaymentdate should also update to receipt.paymentdate.
Related
I have a table named Blacklist and table named Order.
Both have CustomerId column.
Stored Procedure ExecOrder manipulates Order table.
My goal is to exclude Orders that have Blacklisted CustomerId (meaning : Order's CustomerId is in Blacklist table).
I edited ExecOrder SP like this:
DECLARE #Temp TABLE
(
CustomerId UNIQUEIDENTIFIER
);
INSERT INTO #Temp
SELECT RightSide
FROM Blacklist
WHERE LeftSide = #CustomerId;
BEGIN
DECLARE db_cursor CURSOR
FOR SELECT OrderId
FROM dbo.[Order] ord
LEFT OUTER JOIN #Temp t ON t.CustomerId <> ord.CustomerId AND ord.CustomerId <> #CustomerId
WHERE AssetId = #BaseAsset
AND CurrencyId = #QuoteAsset
AND OrderTypeId <> #OrderType
AND [QuotePrice] <= #QuotePrice
AND OrderStatusId = 10
AND Amount > ISNULL(AmountFilled, 0)
AND OrderId < #OrderId
AND OrderBookId = #OrderBookId
AND DeliveryStart = #DeliveryPeriodStart
AND DeliveryEnd = #DeliveryPeriodEnd
AND #MinAmount <= Amount - ISNULL(AmountFilled, 0)
ORDER BY OrderDate;
END;
#Temp table returns correct list of CustomerIds.
Problem is that blacklisted orders are not excluded.
Your #Temp table is holding blacklisted customers.
-- contains blacklisted customer
INSERT INTO #Temp
SELECT RightSide
FROM Blacklist
WHERE LeftSide = #CustomerId;
Now, you need to select orders of customers not existing in #Temp table.
-- You need to select orders, where customerId not exists in #Temp table
SELECT OrderId
FROM dbo.[Order] ord
WHERE NOT EXISTS(SELECT 1 from #Temp WHERE customerId = ord.CustomerId) ...
You should connect with blacklist and take only where connected blacklist not existst:
LEFT OUTER JOIN #Temp t ON t.CustomerId <> ord.CustomerId AND ord.CustomerId <> #CustomerId
Change to:
LEFT OUTER JOIN #Temp t ON t.CustomerId = ord.CustomerId OR ord.CustomerId = #CustomerId
Next you should check if it is empty:
WHERE ... AND t.CustomerId IS NULL
So I understand recursive triggers. Got to be careful of deadlocks etc. However this is only after an insert not after insert and update. Also, I have an audit trigger table that I am updating to make sure all is well. And querying after to double check. All looks fine but no update happens.
if exists (select 'a' from sys.triggers where name = 'invoicememologic')
begin
drop trigger invoicememologic
end
go
create trigger invoicememologic
on transactiontable
after insert
as
begin
declare #inum varchar(1000)
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum) b
while #inum is not null
begin
declare #rCount int
select #rCount = count(*)
from transactiontable
where TransactionInvNum = #inum
if #rCount = 1
begin
declare #tid varchar(100)
select #tid = transactionid
from transactiontable
where TransactionInvNum = #inum
declare #pmemo varchar(MAX)
select #pmemo = p.projectMemo
from transactiontable tt
inner join project p on left(tt.projectid, charindex(':', tt.projectid)) = p.projectid
where transactionInvNum = #inum
insert into audittrigger
values (#pmemo, #tid)
update transactiontable
set transactionmemo2 = #pmemo
where ltrim(rtrim(transactionid)) = ltrim(rtrim(#tid))
end
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where abs(transactionjointinv) = 1
and right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum ) a
where transactioninvnum > #inum
end
end
Reason for trigger. 1 Invoice can be multiple rows in the database. 3 rows. So it only should update any one of the 3 rows. Doesn't matter. And it must grab the memo from the parent project of the phases that are being inserted into the database. hence the inner join on the left with charindex.
So I check the audit table. All looks well there. MEMO is there and the transactionid is there. I query after the trigger fires. TransactionID exists in the transactiontable but the memo2 is not being updated.
TransactionMemo2 is type of ntext. I thought it might be varchar with a direct update command will fail. I tried to update manually through setting a var as the text string and call the update manually with the transactionid being required. all worked fine. I am lost
I'm trying to use the below query to update some data only if #rowcount is greater than 0. However, its executing the update statement even when #RowCount is 0. Can someone help whats wrong in here please? I would like to do nothing if #RowCount is 0.
I'm using SQL Server 2014.
TRUNCATE TABLE Count1
DECLARE #RowCount AS INT
--insert data in a temporary table
SELECT YEAR, S_ID
into #Count1 FROM
(SELECT DISTINCT D.YEAR, S_ID FROM SALES S JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID AND PT.TRANSACTION_TYPE = 'BILLING'
JOIN DATE D ON D.DAY = S.DAY AND PT.DAY = S.DAY
WHERE
S.SALES_CUSTOMER != PT.CUSTOMER)Counter1;
--Store the rowcount in a temporary variable
SET #RowCount = (SELECT Count(*) FROM #Count1)
--Fix the data with differences only if count>0
IF ##ROWCOUNT > 0
BEGIN
UPDATE SALES
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
JOIN TRANSACTIONS PT ON S.COMBINED_TXN_ID = PT.S_ID
JOIN DATE D ON D.DAY = S.DAY AND PT.DAY = S.DAY
WHERE
S_ID IN (SELECT S_ID FROM #Count1)
END;
##ROWCOUNT Returns the number of rows affected by the last statement.
Change system variable ##ROWCOUNT by your own variable #RowCount
--Store the rowcount in a temporary variable
SET #RowCount = (SELECT Count(*) FROM #Count1)
--Fix the data with differences only if count>0
IF #RowCount > 0
You are confusing the local variable #RowCount you defined yourself, with the global variable ##ROWCOUNT Microsoft defined for you (which you can't set).
So simply refer to the correct variable. Better yet: rename your local variable to avoid such confusion.
You don't even need the if at all, since the update wouldn't be executed if #Count1 is empty as there are no records to fulfill the where clause (WHERE S_ID IN ({empty result set})).
If you wish to use the same variable you have declared use #rowcount
DECLARE #recordCount INT
--insert data in a temporary table
SELECT YEAR
,S_ID
INTO #Count1
FROM (
SELECT DISTINCT D.YEAR
,S_ID
FROM SALES S
INNER JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID
AND PT.TRANSACTION_TYPE = 'BILLING'
INNER JOIN DATE D
ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S.SALES_CUSTOMER != PT.CUSTOMER
) Counter1;
--Store the rowcount in a temporary variable
SELECT #recordCount = count(*)
FROM #Count1
--Fix the data with differences only if count>0
IF (#recordCount > 0)
BEGIN
UPDATE SALES
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
INNER JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID
INNER JOIN DATE D
ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S_ID IN (
SELECT S_ID
FROM #Count1
)
END
You could skip all the temp tables and checks and just do an update. Something like this should be the same thing as all the code you posted.
UPDATE S
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
JOIN S PT ON S.COMBINED_TXN_ID = PT.S_ID
AND PT.TRANSACTION_TYPE = 'BILLING'
JOIN DATE D ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S.SALES_CUSTOMER != PT.CUSTOMER
How do I count the number of distinct rows minus a join over those same distinct rows?
I'm writing after triggers where I need to raise an error if the user does not have rights to the rows submitted. I can do this in two statements but this seems inefficient.
DECLARE #AccessibleCount INT =
(
SELECT
COUNT(DISTINCT i.[ParentId])
FROM
inserted i
INNER JOIN [SuperSecret].[Parent] AS p ON
p.[Id] = i.[ParentId] AND
p.[LockedBy] = #UserId
);
DECLARE #ActualCount INT = (SELECT COUNT(DISTINCT [ParentId]) FROM inserted);
IF (#AccessibleCount <> #ActualCount)
BEGIN
RAISERROR(...);
ROLLBACK TRANSACTION;
END
For performance sake, it seems like I should use a subquery over the distinct inserted.ParentId for both counts. I tried the following but it resulted in "Invalid object name 'i'."
DECLARE #ActualMinusAccessible INT =
(
SELECT
COUNT(*)
-
(
SELECT
COUNT(*)
FROM
i
INNER JOIN [SuperSecret].[Parent] AS p ON
p.[Id] = i.[ParentId] AND
p.[LockedBy] = #UserId
)
FROM
(
SELECT DISTINCT [ParentId] FROM inserted
) AS i
);
IF (#ActualMinusAccessible <> 0)
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END
If am not wrong you want to Raise Error if a [ParentId] is inserted which is not present in [SuperSecret].[Parent] table. Try changing your SQL query like this.
IF EXISTS (SELECT 1
FROM inserted i
WHERE NOT EXISTS (SELECT 1
FROM [SuperSecret].[Parent] a
WHERE i.[ParentId] = a.[ParentId] AND a.[LockedBy] = #UserId))
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END
OR
IF (SELECT Count(DISTINCT [ParentId]) - (SELECT Count(DISTINCT i.[ParentId])
FROM inserted i
INNER JOIN [SuperSecret].[Parent] AS p
ON p.[Id] = i.[ParentId]
AND p.[LockedBy] = #UserId)
FROM inserted) <> 0
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END
Below is a SQL Server 2005 update trigger. For every update on the email_subscriberList table where the isActive flag changes we insert an audit record into the email_Events table. This works fine for single updates but for bulk updates only the last updated row is recorded. How do I convert the below code to perform an insert for every row updated?
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
DECLARE #CustomerId int
DECLARE #internalId int
DECLARE #oldIsActive bit
DECLARE #newIsActive bit
DECLARE #email_address varchar(255)
DECLARE #mailinglist_name varchar(255)
DECLARE #email_event_type varchar(1)
SELECT #oldIsActive = isActive from Deleted
SELECT #newIsActive = isActive from Inserted
IF #oldIsActive <> #newIsActive
BEGIN
IF #newIsActive = 1
BEGIN
SELECT #email_event_type = 'S'
END
ELSE
BEGIN
SELECT #email_event_type = 'U'
END
SELECT #CustomerId = customerid from Inserted
SELECT #internalId = internalId from Inserted
SELECT #email_address = (select email from customer where customerid = #CustomerId)
SELECT #mailinglist_name = (select listDescription from Email_lists where internalId = #internalId)
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
VALUES
(#internalId, getDate(), #email_address, #email_event_type,#mailinglist_name)
END
example
untested
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
SELECT i.internalId,getDate(),c.email,
case i.isActive when 1 then 'S' else 'U' end,e.listDescription
from Inserted i
join deleted d on i.customerid = d.customerid
and i.isActive <> d.isActive
join customer c on i.customerid = c.customerid
join Email_lists e on e.internalId = i.internalId
Left outer joins, for in case there are no related entries in customer or email_Lists (as is possible in the current code) -- make them inner joins if you know there will be data present (i.e. foreign keys are in place).
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
select
i.InternalId
,getdate()
,cu.Email
,case i.IsaActive
when 1 then 'S'
else 'U'
end
,el.ListDescription
from inserted i
inner join deleted d
on i.CustomerId = d.CustomerId
and i.IsActive <> d.IsActive
left outer join Customer cu
on cu.CustomerId = i.CustomerId
left outer join Email_Lists el
on el.InternalId = i.InternalId
Test it well, especially for concurrency issues. Those joins within the trigger make me nervous.