UPDATE used ROWID or ROWNUM - sql

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.

Related

Is it possible to create and execute 2 triggers on one table

I am trying to find some workaround to create and execute multiple triggers for different tables on one table. I have 2 tables
Person Detail
Address
The way this table is designed is when a user updates Person's address it creates a new record into address table instead of updating the existing one and I want to insert the changes into auditlog table when a user updates person details or address.
I was able to create a trigger for person table but dont know how can i make it work by using or calling multiple triggers on Person Table
Following is the code for Person table update trigger
CREATE TRIGGER [dbo].[tr_tblPersonDetail_ForUpdate]
ON [dbo].[PersonDetail]
FOR Update
AS
BEGIN
Declare #Id int,
Declare #OldFirstName varchar(50), #NewFirstName varchar(50)
BEGIN
Select #Id=personId,#NewFirstName = NewFirstName from Inserted
select #OldFirstName = NewFirstName from deleted where #id = personId
if(#OldFirstName <> #NewFirstName )
Insert into AuditLog values('some value','AfterValue','Before Value')
Yes, one table may have multiple triggers. You do need to exercise caution, however - you may not want every update to fire both triggers, or you may have need to execute the triggers in a particular order.
It really sounds like you want an INSTEAD OF UPDATE trigger for the requirement of
creates a new record into address table instead of updating the existing one
If you need your trigger to execute only if specific field(s) were updated, use UPDATED() to test for them.
You can use sp_settriggerorder to dictate which trigger should execute and which one last.
In your case, I would suggest an AFTER UPDATE for any operation that changes only the person's name, and then an INSTEAD OF UPDATE which handles cases where the address is changed (which will also need to handle the name change).
If you choose to continue down the path of using triggers at all.

Trigger or stored procedure

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

update multiple tables with trigger

I want to update two tables when a user wants to update a view.
create trigger update_mID
instead of update of mID on LateRating
for each row
begin
update Movie, Rating
set mID = new.mID
where mID = Old.mID;
end;
I want to update bot the Movie relation and the Rating relation, however, I have not yet experienced a trigger that is able to update multiple tables. Can someone please indicate how I can overcome this?
UPDATE: This is for a exercise to test my trigger scripting skills. The requirement is that I have to write it in one trigger query. #CL. I tried putting two update statements between the begin and end keywords, however, it says that there is a syntax error.... is there a specific way to put two updates between the begin and end?
A single UPDATE statement can modify only a single table.
Use two UPDATEs:
UPDATE Movie SET mID = NEW.mID WHERE mID = OLD.mID;
UPDATE Rating SET mID = NEW.mID WHERE mID = OLD.mID;
You could do a REPLACE INTO statement like the following:
DROP TRIGGER IF EXISTS `update_mID`;CREATE DEFINER=`USER`#`localhost` TRIGGER
`update_mID` AFTER UPDATE ON `tblname` FOR EACH ROW REPLACE INTO
USER_DATABASENAME.TBLNAME (COLUMNNAME1,COLUMNNAME1) SELECT COLUMNNAME1,COLUMNNAME1
FROM USER_DBNAME.TBLNAME
This can even be two separate databases like the example below:
DROP TRIGGER IF EXISTS `update_mID`;CREATE DEFINER=`USER`#`localhost` TRIGGER
`update_mID` AFTER UPDATE ON `tblname from DB1` FOR EACH ROW REPLACE INTO
USER_DATABASENAME1.TBLNAMEDB2 (COLUMNNAME1,COLUMNNAME1) SELECT
COLUMNNAME1,COLUMNNAME1 FROM USER_DBNAME2.TBLNAME

infinite trigger loop....by design(!). How to work-around?

I know I'm going to get flamed for this, but....
I have table ProductA, ProductB, and ProductC which have very similar schema but for 2 or 3 columns in each. Each table has an insert trigger which fires a duplicate row for each insert in A, B, or C to table Products, which is a consolidation of all products. In addition, update triggers on A,B, or C will likewise update their equivalent row in Table Products, as do delete triggers. All working flawlessly until.....we update, say, Table Products Column A, which also exists in Table A, B, and C.
I'm looking to develop a trigger on Table Products that will propogate that update in Column A to Column A in each of tables A, B, and C, BUT, without invoking the update triggers on Tables A, B, and C. The desired behavior is for updates to work in both directions without incurring an endless loop.(Note, only 2 columns in table products need to be replicated BACK to tables A, B, and C)
Options are:
redesign the schema so this situation doesn't exist (not in the
cards, this is a quick solution, redesign can be done by someone
else);
Manually disable the triggers when I update table products
(this is all done at the application level, users won't have the
ability to log into SSMA and disable triggers when they update table
products);
Come to Stack Overflow and hope someone has already encountered this type of problem!
Conceptually, how could this be done?
6/7 Update:
Here is the trigger code on Table A (e.g):
ALTER TRIGGER [dbo].[GRSM_WETLANDS_Point_GIS_tbl_locations_update]
ON [dbo].[GRSM_WETLANDS_POINT]
after update
AS
BEGIN
SET NOCOUNT ON;
update dbo.TBL_LOCATIONS
set
X_Coord = i.X_Coord,
Y_Coord = i.Y_Coord,
PlaceName = i.PlaceName,
FCSubtype = case
when i.FCSubtype = 1 then 'Point: Too Small to Determin Boundary'
when i.FCSubtype = 2 then 'Point: Boundary Determined by Contractor but not Surveyed'
when i.FCSubtype = 3 then 'Point: Wetland Reported but not yet Surveyed'
end ,
Landform = i.Landform
from dbo.TBL_LOCATIONS
Join inserted i
on TBL_LOCATIONS.GIS_Location_ID = i.GIS_Location_ID
end
GO
And
ALTER TRIGGER [dbo].[GRSM_WETLANDS_POINT_GIS_tbl_locations]
ON
[dbo].[GRSM_WETLANDS_POINT]
after INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.TBL_LOCATIONS(
X_Coord, Y_Coord,
PlaceName,
FCSubtype, Landform
)
SELECT
a.X_Coord, a.Y_Coord,
a.PlaceName,
a.FCSubtype, a.Landform
From
(
SELECT
X_Coord, Y_Coord,
PlaceName,
FCSubtype = case
when FCSubtype = 1 then 'Point: Too Small to Determin Boundary'
when FCSubtype = 2 then 'Point: Boundary Determined by Contractor but not Surveyed'
when FCSubtype = 3 then 'Point: Wetland Reported but not yet Surveyed'
end ,
Landform
FROM inserted
) AS a
end
GO
And here is the currently disabled update trigger on table products:
ALTER TRIGGER [dbo].[tbl_locations_updateto_geo]
ON [dbo].[TBL_LOCATIONS]
for update
AS
BEGIN
--IF ##NESTLEVEL>1 RETURN
SET NOCOUNT ON;
update dbo.GRSM_Wetlands_Point
set
X_Coord = i.X_Coord,
Y_Coord = i.Y_Coord,
PlaceName = i.PlaceName,
FCSubtype = i.FCSubtype,
Landform = i.Landform,
from dbo.TBL_LOCATIONS
Join inserted i
on TBL_LOCATIONS.GIS_Location_ID = i.GIS_Location_ID
where TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Made by GPS Survey'
or TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Derived from NWI'
or TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Made by Other Means'
or TBL_LOCATIONS.FCSubtype = 'Polygon: Legal Jurisdictional Determination';
end
GO
(tbl names changed to keep with the posting text)
There are two types of recursion, direct and indirect: http://msdn.microsoft.com/en-us/library/ms190739.aspx
You can use the RECURSIVE_TRIGGERS option to stop direct recursion, but your case is indirect recursion so you'd have to set the nested triggers option. This will fix your problem, but if anything else in the system relies on recursion then it won't be a good option.
USE DatabaseName
GO
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'nested triggers', 0
GO
RECONFIGURE
GO
EDIT in response to your updated post:
I almost hate to give you this solution because you're ultimately taking a really crappy design and extending it... making even more of a mess than it is already instead of taking the time to understand what's going on and just fixing it. You should honestly just create another table to hold the values that need to be in sync between the two tables so the data is only in one place, and then relate those tables to that one through a key. But nonetheless...
You need a flag to set you're updating in one trigger so the other trigger can abort its operation if it sees it's true. Since (as far as I know) you can only have locally scoped variable, that means you'll need a table to store this flag value in and look it up from.
You can implement this solution with varying levels of complexity, but the easiest way is to just have all triggers set the flag to true when starting and false when ending. And before they start they check the flag and stop executing if it's true;
The problem with this is that there could be another update that isn't related to a trigger happening at the same time and it wouldn't get propogated to the next table. If you want to take this route, then I'll leave it up to you to figure out how to solve that problem.

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