Run update statement on inserted rows only through a trigger - sql

I have created a Trigger to update a custom date [Invoice.ContractStartDate] on an invoice record when added to the Invoice Table [InvoiceTable].
The Trigger below works but I would like this to run for Inserted records only and avoid using the WHERE clause for DateTimeCreated.
I will still require the WHERE clause for Contract.Days <> ' '
Is this possible using using AFTER INSERT or INSTEAD OF INSERT?
CREATE TRIGGER UpdateContractStartDate ON InvoiceTable
FOR INSERT AS
UPDATE InvoiceTable
SET InvoiceTable.ContractStartDate = InvoiceTable.InvoiceDate + Contract.Days
FROM Contract
INNER JOIN InvoiceTable
ON InvoiceTable.ContractID = Contract.ContractID
WHERE (CAST(InvoiceTable.DateTimeCreated AS DATE) = CAST(GETDATE() AS DATE))
AND (Contract.Days <> '')

You don't provide any information on your table schema, but assuming you have a unique key "Id"
The typical syntax would be
Update it set
it.ContractStartDate = it.InvoiceDate + c.Days
from inserted i
join InvoiceTable it on it.Id=i.Id
join Contract c on c.ContactId=it.ContractId and c.Days <> ''

Related

T-SQL trigger for multiple items - multiple updates does not work as i expected

Excuse my sql code as my experience is still somewhat lacking.
I have a trigger to update data in a column of another table even if multiple inserts are made. The trigger works but only for 1 item even if i insert two records. I hade added a select of the memotemp and a select of the trigger query and they show the number of items i expect (2 records for the memotemp table) and (1 records for a select of the trigger query, because 1 order number is added to the memo field and there for it still shows the select of 1 records because of the where statement) just as i expected. What stumps me if why it doesn't update the memo column twice if it has 2 records. The method of joining the inserted with the appropriate table for a delete trigger works perfectly, always deletes as many records as expected one or several at once.
This is the trigger code.
-- insert production order number into purchase order internal memo
select
i.vrr_order_id, i.purchase_order_id
into #memotemp
from inserted i
update po
set po.internal_memo = isnull(po.internal_memo, '') +
case when po.internal_memo is null then (select convert(nvarchar(20), vo.order_number)
from vrr_order vo
where vo.vrr_order_id = t.vrr_order_id)
else (po.internal_memo + ' ' + (select convert(nvarchar(20), vo.order_number)
from vrr_order vo
where vo.vrr_order_id = t.vrr_order_id))
end
from purchase_order po
join #memotemp t on po.purchase_order_id = t.purchase_order_id
where isnull(po.internal_memo, '') not like '%' +
convert(nvarchar(20), (select vo.order_number
from vrr_order vo
where vo.vrr_order_id = t.vrr_order_id)) + '%'
I'm not sure exactly what you are trying to achieve, nor exactly how your tables relate to each other, but your query can be written much more simply
update po
set po.internal_memo =
concat(
po.internal_memo,
po.internal_memo + ' ',
vo.order_number
)
from purchase_order po
join inserted t on po.purchase_order_id = t.purchase_order_id
join vrr_order vo on vo.vrr_order_id = t.vrr_order_id
where isnull(po.internal_memo, '') not like '%' + convert(nvarchar(20), vo.order_number) + '%';
Assuming your join condition is not unique, you cannot aggregate using multiple rows in a joined update. SQL Server will only use a single row to update, and ignore the rest. Exactly which row it uses is arbitrary. This is documented here.
Instead, aggregate first. I'm guessing here, because I don't have your table schema
update po
set po.internal_memo = vo.order_number
from purchase_order po
join inserted t on po.purchase_order_id = t.purchase_order_id
cross apply (
select
order_number = string_agg(vo.order_number, ' ')
from vrr_order vo
where vo.vrr_order_id = t.vrr_order_id
group by vo.vrr_order_id
) vo
where isnull(po.internal_memo, '') not like '%' + convert(nvarchar(20), vo.order_number) + '%';

SQL Trigger insert problems

I want to make a trigger where I can check if the value from the stock of a product 0 is.
Then the trigger should make the value 1.
My Trigger
CREATE TRIGGER [IfStockIsNull]
ON [dbo].[Products]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
if [Products].Stock = 0
UPDATE [Products] SET [Stock] = 1
FROM [Products] AS m
INNER JOIN inserted AS i
ON m.ProductId = i.ProductId;
ELSE
raiserror('Niks aan het handje',10,16);
END
I'm getting the error:
Altering [dbo].[IfStockIsNull]...
(53,1): SQL72014: .Net SqlClient Data Provider: Msg 4104, Level 16, State 1,
Procedure IfStockIsNull, Line 7 The multi-part identifier "Products.Stock"
could not be bound.
(47,0): SQL72045: Script execution error. The executed script:
ALTER TRIGGER [IfStockIsNull]
ON [dbo].[Products]
FOR DELETE, INSERT, UPDATE
AS BEGIN
IF [Products].Stock = 0
UPDATE [Products]
SET [Stock] = 1
FROM [Products] AS m
INNER JOIN
inserted AS i
ON m.ProductId = i.ProductId;
ELSE
RAISERROR ('Niks aan het handje', 10, 16);
END
An error occurred while the batch was being executed.
Maybe you guys can help me out?
A number of issues and surprises in what you're trying to run here. But basically, don't try to run procedural steps when you're dealing with sets. inserted can contain 0, 1 or multiple rows, so which row's stock are you asking about in your IF?
Better to deal with it in the WHERE clause:
CREATE TRIGGER [IfStockIsNull]
ON [dbo].[Products]
FOR INSERT, UPDATE --Removed DELETE, because ?!?
AS
BEGIN
UPDATE [Products] SET [Stock] = 1
FROM [Products] AS m
INNER JOIN inserted AS i
ON m.ProductId = i.ProductId;
WHERE m.Stock = 0
--Not sure what the error is for - the above update may have updated
--some number of rows, between 0 and the number in inserted.
--What circumstances should produce an error then?
END
Simple demo script that the UPDATE is correctly targetting only matching rows from inserted:
declare #t table (ID int not null, Val int not null)
insert into #t(ID,Val) values (1,1),(2,2),(3,3)
update
#t
set
Val = 4
from
#t t
inner join
(select 2 as c) n
on
t.ID = n.c
select * from #t
Shows that only the row with ID of 2 is updated.
Even Microsoft's own example of UPDATE ... FROM syntax uses the UPDATE <table> ... FROM <table> <alias> ... syntax that Gordon asserts doesn't work:
USE AdventureWorks2012;
GO
UPDATE Sales.SalesPerson
SET SalesYTD = SalesYTD + SubTotal
FROM Sales.SalesPerson AS sp
JOIN Sales.SalesOrderHeader AS so
ON sp.BusinessEntityID = so.SalesPersonID
AND so.OrderDate = (SELECT MAX(OrderDate)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID = sp.BusinessEntityID);
GO
This example does have an issue which is further explained below it but that relates to the "multiple matching rows in other tables will result in only a single update" flaw that anyone working with UPDATE ... FROM should make themselves aware of.
If you want to stop the insert if any rows are bad, then do that before making the changes:
ALTER TRIGGER [IfStockIsNull] ON [dbo].[Products]
FOR INSERT, UPDATE -- DELETE is not really appropriate
AS BEGIN
IF (EXISTS (SELECT 1
FROM Products p JOIN
inserted i
ON m.ProductId = i.ProductId
WHERE p.Stock = 0
)
)
BEGIN
RAISERROR ('Niks aan het handje', 10, 16);
END;
UPDATE p
SET Stock = 1
FROM Products p INNER JOIN
inserted AS i
ON p.ProductId = i.ProductId;
END;
Notes:
The comparison in the IF checks if any of the Product rows have a 0. If so, the entire transaction is rolled back.
You might complain that you cannot reject a single row. But that is how transactions work. If any row fails in an INSERT, the entire INSERT is rolled back, not just a single row.
The UPDATE is not correct, because you have Products in the FROM clause and the UPDATE clause. You need to use the alias for the first one.

PL/SQL Update Query

Attempting to update a table I have created with null values from another table I have created in PL/SQL:
I was able to utilize the below update in SQL Server but am running into issues within PL/SQL (dont ask why I am running it in both)
Overall_Inventory = Table created with some populated values and some null valuse; this is the table requiring updates to those null values
task_table = Table also created, but contains value needing to be updated into T1
update dbh.overall_inventory
set dbh.overall_inventory.case_due_date = tsk.TASK_ACTION_TIMESTAMP
from dbh.overall_inventory,
(SELECT tsk.INQ_KEY,
min(tsk.TASK_ACTION_TIMESTAMP) as TASK_ACTION_TIMESTAMP
FROM dbh.task_table tsk
inner join dbh.overall_inventory Inv
on tsk.INQ_KEY = inv.inq_key
where tsk.ACTION_CD = '324'
group by tsk.INQ_KEY
) tsk
where tsk.INQ_KEY = dbh.overall_inventory.inq_key`
Oracle doesn't support from clause in the update statement. In this situation merge statement can be used.
merge into overall_inventory oi
using (select tsk.inq_key,
min(tsk.task_action_timestamp) as task_action_timestamp
from task_table tsk
join overall_inventory Inv
on tsk.inq_key = inv.inq_key
where tsk.action_cd = '324'
group by tsk.inq_key) tsk
on (tsk.inq_key = oi.inq_key )
when matched then
update
set case_due_date = tsk.task_action_timestamp
where case_due_date is null -- as I understood only NULL values
-- need to be updated
Note: Not tested because no sample data and desired result were provided.
I think you're looking for update over a select:
UPDATE (
  SELECT product_id, category_id
  FROM product) st
SET st.category_id = 5
WHERE st.category_id = 4;

TSQL: Prevent Nulls update over existing data

I have 2 tables:
Orders (Table I update and want keep existing data, and prevent overwriting with nulls)
WRK_Table (This table is identical to Orders but can not guarantee all columns will have data when running update)
PK column 'Master_Ordernum'
There are many columns in the tables, the WRK_Table will have the PK, but data in other columns can not be counted on.
I want the WRK_Table to update Orders only with actual data and not Nulls.
I was thinking along this line:
UPDATE [Orders]
SET [Status] = CASE WHEN S.[Status] IS NOT NULL THEN S.[Status] END
FROM [WRK_TABLE] S
WHERE [Orders].[Master_Ordernum] = S.[Master_Ordernum]
The existing data in Orders is being updated with nulls
does this help?
update orders set name = work.name
from orders
inner join work on orders.id = work.id and work.Name is not null
Not the same col / table name but you should get the idea
EDIT
Your SQL, NOT TESTED
UPDATE Orders
SET [Status] = WRK_Table.[Status]
FROM [Orders]
inner join WRK_Table on [Orders].[Master_Ordernum] = [WRK_Table].[Master_Ordernum] and WRK_Table.[Status] is not null
If you want to test and undo your data just do something like (assuming you don't have 100s of rows)
begin tran
/* .. SQL THAT UPDATES ... */
select * from orders // review the data
rollback tran // Undo changes
If you're going to use a Case statement, you need to include an else to prevent it from just inserting that null. Try this - if the work table has a null, let it just overwrite the value with the existing value.
UPDATE [Orders]
SET [Status] = CASE WHEN S.[Status] IS NOT NULL THEN S.[Status] else [Orders].[Status] END
FROM [WRK_TABLE] S
WHERE [Orders].[Master_Ordernum] = S.[Master_Ordernum]
Not sure if you can use
Update my_table set col = ISNULL(#newval,#existingval) where id=#id
https://msdn.microsoft.com/en-us/library/ms184325.aspx

How can I do a Trigger in SQL?

I am trying to learn about triggers, I know how to do really basic ones, but I can't wrap my head around this. I have two tables Services(master) and Sales.
Services(ServiceID,ServiceCost,SalesTotal)
Sales(TransactionID,TransactionDate,Amount,ServiceID)
I am trying to write trigger for Update,Delete,Insert. When ever you enter a new sale in the Sales table, the SalesTotal will get updated in the Services table according to ServiceID.
ex:
INSERT INTO Sales(TransactionID,TransactionDate,Amount,ServiceID)
VALUES ('16','2014-11-19','50','101');
So if the SalesTotal for TransactionID '101' was 1000, after the insert it would be 1050 and the opposite if I deleted/updated.
I think I have to use join tables, but I am currently stumped.
use this trigger, or split insert / dalete / update:
Code:
CREATE TRIGGER [dbo].[t_Update_Services] on Sales AFTER UPDATE,INSERT, DELETE
AS
BEGIN
UPDATE a
SET SalesTotal = SalesTotal - b.Amount
FROM Services a
JOIN deleted b ON a.ServiceID = b.ServiceID
UPDATE a
SET SalesTotal = SalesTotal + b.Amount
FROM Services a
JOIN inserted b ON a.ServiceID = b.ServiceID
END
Add this inside the trigger
BEGIN
if exists (select * from deleted)
begin
UPDATE Services
SET SalesTotal = SalesTotal - deleted.Amount
FROM Services
JOIN deleted ON Services.ServiceID = deleted.ServiceID
end
else
begin
UPDATE Services
SET SalesTotal = SalesTotal + inserted .Amount
FROM Services
JOIN inserted ON Services.ServiceID = inserted.ServiceID
end
END