Creating a trigger on [msdb].[dbo].restorehistory - sql

I have a requirement to update or change the values of some fields in specific tables when someone restores a Prod database to a Test environment. I just want to change for example 'PROD' characters to 'Test' characters in certain fields once a database have been restored from Prod to Test environment. I have just created a small DB with two tables only and a trigger on [msdb].[dbo].restorehistory table, backed up the sample DB to simulate the scenario. But whenever I try restore there is nothing happening on the trigger.
From the below this is what I tried but it seems not working. It looks like the trigger is not hit when there is an [INSERT] event to the table[restorehistory].
USE [msdb]
GO
/****** Object: Trigger [dbo].[TriggerA] Script Date: 12/24/2022 8:22:59 PM ******/
CREATE TRIGGER [dbo].[TriggerA]
ON [msdb].[dbo].[restorehistory]
FOR INSERT
AS
DECLARE #DatabaseName AS VARCHAR(100)
SELECT #DatabaseName = DESTINATION_DATABASE_NAME
FROM INSERTED
IF #DatabaseName='TestDB'
BEGIN
UPDATE [TestDB].[dbo].<TABLE_X>
SET FIELD_A = REPLACE(FIELD_A,'_character_to_replace_','_replace_with_this_character_'),
FIELD_B = REPLACE(FIELD_B,'_character_to_replace_','_replace_with_this_character_')
WHERE
FIELD_A LIKE '%_character_to_replace_%'
END
Please advise if there is something I'm doing wrong or if there is any better alternative.

Related

INSERTED table not retrieving values inserted after truncating

I have a Trigger that makes changes to a columns value in a table if the value is updated/inserted.
If I do a direct UPDATE statement to the table the Trigger works.
If I run a stored procedure to truncate the table and repopulate the table (data warehouse table/ETL)the trigger does not work.
I am not sure why the trigger is not working after I run the stored procedure. Any ideas? Thank you ahead of time.
HERE is the trigger:
CREATE TRIGGER TriggerName
ON PlanesTable
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #vPlaneID INT;
SELECT #vPlaneID = INSERTED.PlaneID
FROM INSERTED;
UPDATE dwPlanesDim
SET ServiceStatus = CASE WHEN DATEDIFF(DAY, LastService,GETDATE()) <= 30 THEN 'Current'
ELSE 'Review Service'
END
WHERE #vPlaneID = PlaneID
SET NOCOUNT OFF;
END;
GO
TRUNCATE is not an update. In fact technically, it's not even DML. TRUNCATE works so quickly specifically because it doesn't log any individual rows. It just deallocates the disk space used by the table. Consequently, those inserted/deleted tables are not available after a truncation.
Additionally, if you truncate the table and then populate the table, that will be an insert, not an update. Since this trigger is set only to fire after UPDATE, it won't fire in that case.
Not firing any trigger is an expected outcome using a TRUNCATE.
Extract: TRUNCATE TABLE cannot activate a trigger because the operation does not log individual row deletions.
https://learn.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql

What is the process during re-naming and re-creating a MS-SQL table using stored procedure?

I have a table called myTable where continuous insertion is happening. I will rename that table by myTable_Date and create a new table, myTable through a Store Procedure.
I want to know what will happen during re-naming and re-creating the table, will it drop any packet?
SQL Server has sp_rename built in if you just want to change the name of a table.
sp_rename myTable, myTable_Date
Would change the name from myTable to myTable_Date
But it only changes the name reference in sys.Objects so make sure any references are altered and read the documentation about it :)
The Microsoft doc for it is HERE
When you rename the myTable to myTableDate, myTable won't exist anymore so if someone tries to insert something inside myTable it will fail.
When you create new myTable with the same name and columns everything will be fine and the insertion process will continue.
I suggest you to make a little script renaming the table and creating new one. Something like this:
sp_rename myTable, myTable_Date
GO
CREATE TABLE myTable(
-- Table definition
)
When you rename the table you will get warning like this: "Caution: Changing any part of an object name could break scripts and stored procedures." so you better create the new table fast.
Other option is you create a table exact like myTable and insert all data from myTable there and then can delete them from myTable. No renaming, no dropping and insertion process will not be interrupted.
I want to know what will happen during re-naming and re-creating the
table, will it drop any packet?
Inserts attempted after the table is renamed will err until the table is recreated. You can avoid that by executing the tasks in a transaction. Short term blocking will happen if an insert is attempted before the transaction is committed but no rows will be lost. For example:
CREATE PROC dbo.ReanmeMytableWithDate
AS
DECLARE #NewName sysname = 'mytable_' + CONVERT(nchar(8), SYSDATETIME(), 112);
SET XACT_ABORT ON;
BEGIN TRY;
BEGIN TRAN;
EXEC sp_rename N'dbo.mytable', #NewName;
CREATE TABLE dbo.mytable(
col1 int
);
COMMIT;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO
I don't know your use case for renaming tables like this but it seems table partitioning might be a better approach as #Damien_The_Unbeliever suggested. Although table partitioning previously required Enterprise Edition, the feature is available in Standard Edition beginning with SQL Server 2016 SP1 as well as Azure SQL Database.

SQL Server update trigger executing twice

I have the following trigger -
USE [DatabaseA]
GO
/****** Object: Trigger [dbo].[T_TableA_U] Script Date: 02/17/2014 18:08:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[T_TableA_U]
on [dbo].[TableA]
after update
as
set nocount on
DECLARE #HistoryType char(1) --"I"=insert, "U"=update, "D"=delete
DECLARE #RevisionID INT
SET #HistoryType = 'U'
SET #RevisionID = 0
INSERT INTO [DatabaseB].[dbo].[TableA]
(column1_revtm, column2_revtype,
column3_id, column4_revid, column5_type, ....)
SELECT
GETDATE(), #HistoryType,
a.column1_id, #RevisionID, a.column2, ....
FROM TableA a
inner join inserted i on a.column1_id = i.column1_id
If I manually update a row in the SOURCE it creates 1 new row in the DESTINATION. This is good.
When I use the 3rd party application I'm building this trigger on however, it's generating duplicate rows in the DESTINATION. All data is exactly the same except for the GETDATE() which tells me it's somehow duplicating the result which the application is forcing.
So how do I get around this? Is there a way to force a DISTINCT before the INSERT happens either in this trigger or could I create another trigger on the DESTINATION table that says if the row is a duplicate then only INSERT 1 of them?
I found the problem to be in the way the legacy application is designed. The table I had this trigger on had a PK/FK matching to another table. I reversed the trigger to run off the table with the FK and it works on a 1 to 1 basis with no duplicates so it would seem the legacy app updates both tables even if only 1 table is updated.

translate from SQL Server to Oracle

I was hoping one of you Oracle experts would be able to give me a hand with this. I have the following SQL Server script, but I need to rewrite it for Oracle:
USE mydb
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE trigger mydb . [CONNECTERTRIGGER] on mydb . [DtreeNotify]
FOR INSERT AS
BEGIN
IF ##ROWCOUNT=0
RETURN
SET IDENTITY_INSERT mydb.DTreeNotify2 ON
INSERT INTO mydb.DTreeNotify2
(NID,NType,DataID,VersionNum,OwnerID,SubType)
SELECT inserted.NID,
inserted.NType,
inserted.DataID,
inserted.VersionNum,
mydb.Dtree.OwnerID,
livelink.DTree.SubType
FROM inserted, livelink.DTree
WHERE inserted.DataID = livelink.DTree.DataID;
END
I think ##rowcount becomes sql%rowcount, but I'm struggling with the identity_insert bit.
I don't think anything else should change. Opinions?
Don't worry about the IDENTITY_INSERT bit, the way it is done in Oracle is so much different that there is no need for such an option anyway. Look for "SEQUENCE" to learn more about that.
Here we go:
CREATE trigger "CONNECTERTRIGGER"
AFTER INSERT on "DtreeNotify"
FOR EACH ROW
begin
insert into DTreeNotify2 (NID,NType,DataID,VersionNum,OwnerID,SubType)
select :new.NID, :new.NType, :new.DataID, :new.VersionNum,
Dtree.OwnerID, livelink.DTree.SubType
from livelink.DTree
where :new.DataID=livelink.DTree.DataID;
end;
Comments: I assume Dtree.OwnerID is a package variable you can read directly.
As you can see, there is a :new record, which contains one inserted record (this trigger is called for each record that gets inserted).
Edit: changed the BEFORE trigger to an AFTER trigger

How can I do a BEFORE UPDATED trigger with sql server?

I'm using Sqlserver express and I can't do before updated trigger. There's a other way to do that?
MSSQL does not support BEFORE triggers. The closest you have is INSTEAD OF triggers but their behavior is different to that of BEFORE triggers in MySQL.
You can learn more about them here, and note that INSTEAD OF triggers "Specifies that the trigger is executed instead of the triggering SQL statement, thus overriding the actions of the triggering statements." Thus, actions on the update may not take place if the trigger is not properly written/handled. Cascading actions are also affected.
You may instead want to use a different approach to what you are trying to achieve.
It is true that there aren't "before triggers" in MSSQL. However, you could still track the changes that were made on the table, by using the "inserted" and "deleted" tables together. When an update causes the trigger to fire, the "inserted" table stores the new values and the "deleted" table stores the old values. Once having this info, you could relatively easy simulate the "before trigger" behaviour.
Can't be sure if this applied to SQL Server Express, but you can still access the "before" data even if your trigger is happening AFTER the update. You need to read the data from either the deleted or inserted table that is created on the fly when the table is changed. This is essentially what #Stamen says, but I still needed to explore further to understand that (helpful!) answer.
The deleted table stores copies of the affected rows during DELETE and
UPDATE statements. During the execution of a DELETE or UPDATE
statement, rows are deleted from the trigger table and transferred to
the deleted table...
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table...
https://msdn.microsoft.com/en-us/library/ms191300.aspx
So you can create your trigger to read data from one of those tables, e.g.
CREATE TRIGGER <TriggerName> ON <TableName>
AFTER UPDATE
AS
BEGIN
INSERT INTO <HistoryTable> ( <columns...>, DateChanged )
SELECT <columns...>, getdate()
FROM deleted;
END;
My example is based on the one here:
http://www.seemoredata.com/en/showthread.php?134-Example-of-BEFORE-UPDATE-trigger-in-Sql-Server-good-for-Type-2-dimension-table-updates
sql-server triggers
T-SQL supports only AFTER and INSTEAD OF triggers, it does not feature a BEFORE trigger, as found in some other RDBMSs.
I believe you will want to use an INSTEAD OF trigger.
All "normal" triggers in SQL Server are "AFTER ..." triggers. There are no "BEFORE ..." triggers.
To do something before an update, check out INSTEAD OF UPDATE Triggers.
To do a BEFORE UPDATE in SQL Server I use a trick. I do a false update of the record (UPDATE Table SET Field = Field), in such way I get the previous image of the record.
Remember that when you use an instead trigger, it will not commit the insert unless you specifically tell it to in the trigger. Instead of really means do this instead of what you normally do, so none of the normal insert actions would happen.
Full example:
CREATE TRIGGER [dbo].[trig_020_Original_010_010_Gamechanger]
ON [dbo].[T_Original]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Old_Gamechanger int;
DECLARE #New_Gamechanger int;
-- Insert statements for trigger here
SELECT #Old_Gamechanger = Gamechanger from DELETED;
SELECT #New_Gamechanger = Gamechanger from INSERTED;
IF #Old_Gamechanger != #New_Gamechanger
BEGIN
INSERT INTO [dbo].T_History(ChangeDate, Reason, Callcenter_ID, Old_Gamechanger, New_Gamechanger)
SELECT GETDATE(), 'Time for a change', Callcenter_ID, #Old_Gamechanger, #New_Gamechanger
FROM deleted
;
END
END
The updated or deleted values are stored in DELETED. we can get it by the below method in trigger
Full example,
CREATE TRIGGER PRODUCT_UPDATE ON PRODUCTS
FOR UPDATE
AS
BEGIN
DECLARE #PRODUCT_NAME_OLD VARCHAR(100)
DECLARE #PRODUCT_NAME_NEW VARCHAR(100)
SELECT #PRODUCT_NAME_OLD = product_name from DELETED
SELECT #PRODUCT_NAME_NEW = product_name from INSERTED
END