Trigger or stored procedure - sql

I need to update table1.field1 From table2.field2 everytime there is a new entry on table2. I created a trigger that does exactly that :
CREATE [dbo].[insert_trg]
on [dbo].[TheCat99]
AFTER INSERT AS
BEGIN
Update therefore.dbo.thecat99
SET factura = (SELECT dbo.pedido.num_factura
From dbo.Pedido
Where dbo.thecat99.pedido=dbo.pedido.num_pedido)
WHERE dbo.thecat99.pedido = ( SELECT dbo.pedido.num_pedido
FROM dbo.pedido
Where dbo.thecat99.pedido = dbo.pedido.Num_Pedido)
AND dbo.thecat99.factura is NULL
END
The trigger Works but it's not in production yet.I've been reading about how triggers are bad and evil, but i cant see how can i do this using stored procedures.
Is the trigger a good idea? If not, how can i do the same with a stored procedure?

Triggers are not bad nor are they evil. They are a great tool (and for some tasks, the only tool) but they do require knowledge and careful use, since they are not as simple to write as they seem at first glance.
As a general rule, Triggers should be as light-weight and effective as possible.
This is because SQL Server will not return control to whoever executed the statement that fired the trigger until the trigger is also completed.
Having said that, your update code could be written like this (that will be more efficient and more readable):
UPDATE t
SET factura = p.num_factura
FROM therefore.dbo.thecat99 t
INNER JOIN dbo.Pedido p ON t.pedido= p.num_pedido
WHERE t.factura IS NULL
However Since you are not using the inserted table, It means that every time any row gets inserted into TheCat99 all the rows where the pedido value matches the num_pedido in Pedido will be used for the update. There is still room for improvement - using the inserted table instead of thecat99 means you will only be working with the records that was just inserted to the thecat99 table, so your code will be far more efficient:
UPDATE t
SET factura = p.num_factura
FROM inserted t
INNER JOIN dbo.Pedido p ON t.pedido= p.num_pedido
WHERE t.factura IS NULL

Related

UPDATE used ROWID or ROWNUM

I want to update CAR_CASE from CAR when I add a new row in HIRE used trigger
create or replace TRIGGER HIRE_CAR_CASE_UPDATE
AFTER INSERT OR UPDATE OF CAR_ID ON HIRE
REFERENCING OLD AS OLD NEW AS NEW
BEGIN
UPDATE CAR SET CAR_CASE =
(SELECT HIRE.CAR_CASE FROM HIRE where HIRE.CAR_ID = CAR.CAR_ID and TO_DATE (HIRE.DATE_) = TO_DATE(sysdate))
WHERE rowid = :NEW.ROWID;
END;
It appears that what you wanted to do was
CREATE OR REPLACE TRIGGER HIRE_CAR_CASE_UPDATE
AFTER INSERT OR UPDATE OF CAR_ID ON HIRE
FOR EACH ROW
BEGIN
UPDATE CAR c
SET c.CAR_CASE = :NEW.CAR_CASE
WHERE c.CAR_ID = :NEW.CAR_ID;
END HIRE_CAR_CASE_UPDATE;
Because I don't know what your database looks like or how it's to be used I can't say if a similar update should be made to the "old" CAR referred to by :OLD.CAR_ID.
Perhaps more to the point, however - this is business logic which really should not be implemented in a trigger. Put this kind of logic in your application, or perhaps put it into a stored procedure which performs all the necessary logic for a particular business task, rather than scattering logic willy-nilly among a bunch of triggers, procedures, individual SQL statements, etc.
Best of luck.

Change column value after INSERT if the value fits criteria?

I have never really worked with Triggers before in MSSQL but I think it'll be what I need for this task.
The structure of the table is as such:
ID|****|****|****|****|****|****|****|TOUROPERATOR
The Tour Operator Code is the code that tells us what company owned the flight we carried out for them. Two of those codes (there are 24 in total) are outdated. Our users requested that those two be changed but the tour operator code is pulled from a database we don't control. The FlightData table however, we do control. So I was thinking a trigger could change the tour operator code if it was one of the two outdated ones, to the correct ones instead respectively when they were inserted.
So I went into good ol' SQL Management Studio and asked to make a trigger. It gave me some sample code and here is my Pseudo Code below:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER ChangeProvider
ON FlightData
AFTER INSERT
AS
BEGIN
IF(TheInsertedValue == Criteria)
UPDATE FlightData
SET TheInsertedValue = NewValue
ENDIF
END
GO
I am not that good with this type of Database Programming so excuse my mistakes.
How would I go about doing this?
You could add a computed column to your table instead of adding a trigger.
Then the new column could just use a case statement to either show
the original TourOperator column value or the new value you wanted.
You'd add a new column to your table like this
TourOperatorCorrect = CASE WHEN TourOperator = 'Whatever value' THEN 'ChangedValue'
--I just want to use what I have already in the TourOperator column
ELSE TourOperator
END AS VARCHAR(50)
Basics of computed columns are here - https://msdn.microsoft.com/en-ie/library/ms188300.aspx
Your misconception here is that the trigger runs once per inserted value - it is in fact run once per insert statement, so you can and will find more than one row inserted at once.
You'll find that your inserted values are in the pseudo table inserted, which has the same structure as your FlightData table in this case. You write a select statement against that, specifying any criteria you wish.
However, it's not immediately clear what your logic is - does the FlightData table you are updating in your trigger only have one row? Do you update every row in the table with the newest inserted value? It is hard to understand what you are trying to now, and what the purpose of the table and this trigger are - let alone what you would want to do if you inserted more than one row at once.
When inserted table contains mutiple rows,your code will fail,so change code to work with inserted table as whole
UPDATE F
SET f.TheInsertedValue = i.value
from inserted i
join
Flighttable F
on f.matchingcolumn=i.matchingcolumn
and i.somevalue='criteria'

SQL Server : make update trigger don't activate with no changing value

I want to track the update changes in a table via a trigger:
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted
However in real production some of the update queries select rows with vague conditions and update them all regardless of whether they are actually changed, like
UPDATE Targettable
SET customer_type = 'VIP'
WHERE 1 = 1
--or is_obsolete = 0 or register_date < '20160101' something
But due to table size and to analyze, I only want to choose those actually modified data for tracking. How to achieve this goal?
My track table has many columns (so I do not prefer checking inserted and deleted column one by one) but it seldom changes structure.
I guess the following code will be useful.
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT *
FROM Inserted
EXCEPT
SELECT *
FROM Deleted
I realize this post is a couple months old now, but for anyone looking for a well-rounded answer:
To exit the trigger if no rows were affected on SQL Server 2016 and up, Microsoft recommends using the built-in ROWCOUNT_BIG() function in the Optimizing DML Triggers section of the Create Trigger documentation.
Usage:
IF ROWCOUNT_BIG() = 0
RETURN;
To ensure you are excluding rows that were not changed, you'll need to do a compare of the inserted and deleted tables inside the trigger. Taking your example code:
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted i
INNER JOIN deleted d
ON d.[SomePrimaryKeyCol]=i.[SomePrimaryKeyCol] AND
i.customer_type<>d.customer_type
Microsoft documentation and w3schools are great resources for learning how to leverage various types of queries and trigger best practices.
Prevent trigger from doing anything if no rows changed.
Writing-triggers-the-right-way
CREATE TRIGGER the_trigger on dbo.Data
after update
as
begin
if ##ROWCOUNT = 0
return
set nocount on
/* Some Code Here */
end
Get a list of rows that changed:
CREATE TRIGGER the_trigger on dbo.data
AFTER UPDATE
AS
SELECT * from inserted
Previous stack overflow on triggers
#anna - as per #Oded's answer, when an update is performed, the rows are in the deleted table with the old information, and the inserted table with the new information –

If statement on update statement Instead of trigger?

I have a stored procedure where I will calculate factors by merging some tables and updates in table 'tablea'. I have created a trigger on tablea so when there is a update those records will entered into new table 'tablea_new'. My problem with trigger is I will have 6 months factors updated at a time but they some months factors may not updated but they should entered in to new table.So trigger troubled me a lot.
So I went with an If statement to insert update records
My code for If statement is
#Action A
SET A.Factor=B.Net/B.Cost,A.Net=B.Net,A.LastModified=sysDatetime(),A.LastModifiedBy=suser_name(),A.Path=B.Path
FROM Tablea A
INNER JOIN ##TEMP3 B ON
A.Year=B.Year AND
A.Month=B.Month AND
A.Media=B.Media
IF #Action='UPDATE'
BEGIN
INSERT INTO Tablea_New (ID,Media,Year,Month,Factor,Net,UpdatedDate,UpdatedBy,FilePath)
SELECT ID,Media,Year,Month,Factor,Net,LastModified,LastModifiedBy,FilePath FROM Tablea
WHERE Media='CNN'AND YEAR=#YEAR AND Net >1
END
Can you guys give me some suggestions is there can I make some modifications or I need to use triggers :(
You can make use of the Change Data Capture (CDC) feature in SQL Server 2008 R2. This will help you to automatically store all changed rows, irrespective of the type of change (i.e. whether it is insert, update or delete).
To enable CDC for your database, use the below script
USE your_database_name
GO
EXEC sys.sp_cdc_enable_db
To enable CDC for your table, use the below script
EXEC SYS.sp_cdc_enable_table
#source_schema = N'your_schema_name',
#source_name = N'your_table_name',
#role_name = NULL
Once CDC is enabled, a new table is created under schema 'cdc' to store the changed rows, along with metadata about the change. You may directly access this table to get the required data.

How to check if a column is being updated in an INSTEAD OF UPDATE Trigger

I am making some tweaks to a legacy application built on SQL Server 2000, needless to say I only want to do the absolute minimum in the fear that it may just all fall apart.
I have a large table of users, tbUsers, with a BIT flag for IsDeleted. I want to archive off all current and future IsDeleted = 1 user records into my archive table tbDeletedUsers.
Moving the currently deleted users is straight forward, however I want a way to move any future users where the IsDeleted flag is set. I could use a standard AFTER trigger on the column however I plan to add some constraints to the tbUser table that would violate this, what I'd like is for my INSTEAD OF UPDATE trigger to fire and move the record to archive table instead?
I guess my question is... is it possible to trigger an INSTEAD OF UPDATE trigger on the update of an individual column? This is what I have so far:
CREATE TRIGGER trg_ArchiveUsers
INSTEAD OF UPDATE ON tbUsers
AS
BEGIN
...
END
GO
If so an example (SQL 2000 compatible) would be much appreciated!
Using the UPDATE(columnname) test, you can check in a trigger whether a specific column was updated (and then take specific actions), but you can't have a trigger fire only on the update of a specific column. It will fire as soon as the update is performed, regardless of the fact which column was the target of the update.
So, if you think you have to use an INSTEAD OF UPDATE trigger, you'll need to implement two kinds of actions in it:
1) insert into tbDeletedUsers + delete from tbUsers – when IsDeleted is updated (or, more exactly, updated and set to 1);
2) update tbUsers normally – when IsDeleted is not updated (or updated but not set to 1).
Because more than one row can be updated with a single UPDATE instruction, you might also need to take into account that some rows might have IsDeleted set to 1 and others not.
I'm not a big fan of INSTEAD OF triggers, but if I really had to use one for a task like yours, I might omit the UPDATE() test and implement the trigger like this:
CREATE TRIGGER trg_ArchiveUsers
ON tbUsers
INSTEAD OF UPDATE
AS
BEGIN
UPDATE tbUsers
SET
column = INSERTED.column,
…
FROM INSERTED
WHERE INSERTED.key = tbUsers.key
AND INSERTED.IsDeleted = 0
;
DELETE FROM tbUsers
FROM INSERTED
WHERE INSERTED.key = tbUsers.key
AND INSERTED.IsDeleted = 1
;
INSERT INTO tbDeletedUsers (columns)
SELECT columns
FROM INSERTED
WHERE IsDeleted = 1
;
END