maintain history through trigger in asp.net [duplicate] - sql-server

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server history table - populate through SP or Trigger?
I am using this trigger
CREATE TRIGGER [dbo].[Band_Level_trg]
-- ALTER TRIGGER [dbo].[Test_PTA_Table_Update_trg]
ON [dbo].[Band_Level]
INSTEAD OF UPDATE
AS
SET NOCOUNT ON
DECLARE #key int
SET #key = (SELECT band_level_id FROM Inserted)
UPDATE Band_Level
SET band_level_name = band_level_name, description = description
WHERE band_level_id = #key
INSERT INTO dbo.Band_Level
(band_level_name, description)
(SELECT band_level_name,description
FROM Inserted)
but i want to show history on another page.it shows history on same page

Given this other question you posted:
maintain history through trigger in asp.net
(which sorry to say, is also horribly explained)
I think I figured out what you want to do. You want to keep a "history" of the changes using this trigger. Also, what I figured is that, you are "showing the history in the same page" because the trigger inserts on the same table you're updating!
The purpose of the history is to do it on ANOTHER table, if not, your history will become actual data of the table you wanna keep a history of.
You should create another table with the same columns and change the trigger accordingly. Just create a Band_Level_History table and change the trigger to save the changes there. That's it. Like this:
Instead of:
INSERT INTO dbo.Band_Level
Put:
INSERT INTO dbo.Band_Level_History
Also, I'd restructure the triggers in a different way. You should REALLY read this article:
https://web.archive.org/web/20210608144836/http://www.4guysfromrolla.com/webtech/091901-1.shtml
It's short so please read it. Also, next time please try to explain things a little better so everyone can understand. I'm aware that you probably have a language barrier but that's OK. Just try and do your best, I'm from Argentina and I can assure you: eventually you'll learn English if you are consistently trying.
Hope this helps

Related

How to to copy hierarchyid subtree

I want to copy a subtree e.g.
into the same table, at the next free most-right spot (in this case at /5/...).
Is there any stored procedure which may help me do this? If not: how would I do it?
I'm relatively new to SQL so any help will be appreciated.
Those values look like hierarchyid values which makes this fairly easy. Here's what I came up with:
declare #foo table (
h hierarchyid
);
insert into #foo (h)
values
('/4/1/'),
('/4/1/1/'),
('/4/1/1/6/'),
('/4/1/1/7/');
select h.ToString(),
h.GetReparentedValue('/4/', '/5/').ToString()
from #foo;
The first two statements are just setup (which, incidentally, will make future questions you ask here more likely to be answered if you provide it yourself!). The last bit shows moving the subtree to a new parent.
To avoid having to hard-code the /5/, I came up with this (based on the same setup as above):
declare #root hierarchyid = '/';
select #root.GetDescendant(
max(h).GetAncestor(
max(h).GetLevel()-1),
NULL
).ToString()
from #foo;
This is an informed guess as I don't know your actual data. But that should get you a long way there.
Lastly, to actually change your data to reflect the values that I'm selecting, you'll have to run an update statement. I'll leave that to you.

SQL Server - Neutralizing a trigger during SP execution

I have two tables, Orders and App.
App is a "helper" table which is populated according to Orders, and then passes the information on via web service to smart phones.
In order to populate App, we have created a parameterized stored procedure which runs at specific times, fluidly passing data from Orders to App.
But some updates to Orders are not caught by this stored procedure, so we were asked to create a trigger on Orders which executes this SP in these specific instances. This, too, works fine.
The problem starts when updates arrive from smart phones to the table App. The same parameterized SP runs "in reverse" to update the fields in Orders, and this works well - except that doing so can fire our supposedly selective trigger, resulting in redundant updates. To demonstrate:
New row in Orders > SP > Row is written in App > App updated by application > SP > Corresponding row in Orders is updated > Trigger catches this update, firing the SP again.
In this chain, only the last step is a problem.
I have tried using DISABLE TRIGGER and ENABLE TRIGGER within the SP to avoid this problem, but this is risky business and certainly cannot be the best possible way.
The solution I'm working on now is by using a field which is updated during application updates to Orders, but is not updated at any other time. For instance:
UPDATE Orders
SET Orders.StartTime = getdate(),
Orders.EndTime = CASE ... END,
Orders.Unique_Field = X
WHERE Orders.ID = #APPID
In standard updates to Orders, the field Unique_Field is not included in any INSERT or UPDATE statements. However, in some updates from App, this field may remain NULL.
My question is: What is the proper and safe way to tell my trigger to ignore any updates that arrive from my SP?
At present, my trigger looks like this:
AFTER UPDATE, INSERT
NOT FOR REPLICATION
AS
BEGIN
DECLARE #BUILDORDERCHECK AS DATETIME
DECLARE #ORDERDATECHECK AS DATETIME
DECLARE #ORDERNO AS INT
DECLARE #CHECKER AS TINYINT
SELECT #BUILDORDERCHECK = I.UpdateRecordDate,
#ORDERDATECHECK = I.OrderDate,
#ORDERNO = I.OrderNo,
#CHECKER = CASE WHEN NOT EXISTS (SELECT Unique_Field FROM Inserted) THEN 1 ELSE 0 END
FROM Inserted I
IF #BUILDORDERCHECK IS NOT NULL
AND #ORDERDATECHECK >= dateadd(day,-2,getdate())
AND #CHECKER = 1
-- Does not fire from BuildOrder
-- Does not fire on tasks older than 2 days
BEGIN
EXECUTE [dbo].[Asp_Apper;1] 0, -- CallCode, DO NOT CHANGE
1, -- Auto,
1, -- AOK,
0, -- CancelMsg,
0, -- TrailerNo
1 -- RejectMsg
END
END
#BUILDORDERCHECK and #ORDERDATECHECK work fine and behave as expected, but I need to find the right way to tell my trigger to check and see if Unique_Field was included in the update statement without being entangled by NULLS. As I said, Unique_Field can be updated by the SP to a value of NULL, so simply checking for NULL doesn't work.
Thanking you all in advance for any thoughts...
EDIT: It's already been pointed out that this trigger seems to ignore cases where more than one row is updated, which is accurate. Usually, we wouldn't build triggers like this; but in this case, updates to Orders are only ever row-by-row, and never in groups. The only time that this isn't the case is when the SP runs, which we want to ignore anyway.
I would use the CONTEXT_INFO and SET CONTEXT_INFO, something like this:
In the trigger, add a check at the top that bails out if a particular context value is set:
IF ISNULL(CONTEXT_INFO(),0x0) = 0x49204C696B6520426967204275747473
RETURN
And then in the (parts of) the stored procedures where you want to take actions that are ignored, just set that same value:
SET CONTEXT_INFO 0x49204C696B6520426967204275747473;
--Code that shouldn't cause the trigger to fire
SET CONTEXT_INFO 0x0
Which keeps things nicely contained (unlike disabling the trigger which has global effects)
Also, I know you've already stated in comments that this trigger only needs to work for single row update but it would be an automatic failure in code review for me for any trigger that doesn't properly deal with multiple rows existing in inserted (or at the very least, checks the number of rows and gives a clear error message if the requirement of single row updates hasn't been fulfilled)

Auto update time in SQL Server

I would like to have two columns in my table to store the add-time and update-time. As the name suggests, the add-time is the time when a row was first added; the update-time is the last time a row was updated. I can implement first by defaulting value to GETDATE(). As for the second, #Jeremy suggested using triggers here:
On Update: Auto Update Date/Time Field
Is there any easier way?
If I implement a trigger, does that mean two UPDATE statements (or one INSERT and one UPDATE in case the row is just created) have to be executed?
Thanks.
EDIT: For the second part of the question, this is the trigger I have in my database:
CREATE TRIGGER [dbo].[TR_AddUpdateTime]
ON [dbo].[AddUpdateTime]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
UPDATE r
SET UpdateTime = GETDATE()
FROM AddUpdateTime r
JOIN inserted i
ON i.Id = r.Id
END
Does this mean that an additional update statement will be executed whenever I make an update to AddUpdateTime table, or MSSQL is smart enough to recognise that I am updating the same record and save both changes at the same time?
Other ways:
Use a stored procedure to wrap the updates
You can do UPDATE MyTable SET ..., UpdatedWhen = DEFAULT...
You need an UPDATE trigger that itself has one more UPDATE. Using a default on the table means you don't need a trigger for INSERT
You could make sure all inserts and updates go through a stored procedure that inserts the time.
No, the insert trigger will modify the values so that it's only one statement.
Edit: For entity framework could you implement the OnSavingChanges event to insert the update-time field (see here)? This is moving the responsibility from the DB to the Code which you may or may not be comfortable with.
In entity framework, you can use the partial class to extend the business logic. In this case, you can use OnPropertyChanged to set the update-time to DateTime.Now. You can use this article on MSDN as a guidance.
1) "Auto update" and "triggers" doesn't really sound like the way to go.
2) SQL Server has a (relatively new) "merge" statement. But that doesn't really sound like what you're looking for, either.
3) Instead:
a) If primary key doesn't exist (if "new"), then INSERT. In this case, first time = last time = GETDATE().
b) Otherwise, if the primary key already exists, then UPDATE. Your update will update only the "last time" column (along with the rest of the fields you need to update for this record.
4) Perhaps you can wrap this logic in a stored procedure?
5) Again - the key is to update BOTH "first time" and "last time*, the FIRST TIME, and then update ONLY "last time" all SUBSEQUENT times.
They might be an easier way but using triggers will be more effective and will guarantee no mater how records inseted or updated (from .net code or direct table inserts/updates), those two fields are populated
To Gurantee that only one trigger get fired each time, combine insert and update trigger
CREATE TRIGGER <trigger name> ON TableA for INSERT,UPDATE
And do conditional checking to distinguish between two actions
IF UPDATE

simple way to know the updates in table

hi just wanted to know is there any way to know if any update happens in column like Ex:Author ID, and once its update happen in column is there any simple way so that all the managers should know that new author update happen in Database or via email, i am a newbie, if anyone help me step by step on this it will be really greatful for me at the appraisel time please help on this..
Thanks
aaru
You need to create a trigger
CREATE TRIGGER reminder2
ON Sales.Customer
AFTER INSERT, UPDATE, DELETE
AS
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'AdventureWorks Administrator',
#recipients = 'danw#Adventure-Works.com',
#body = 'Don''t forget to print a report for the sales force.',
#subject = 'Reminder';
However, using the sp_send_dbmail is not appropriate from within a stored procedure as it will slow down the update of a row. Meaning that everytime an update is done to the row, it will have to wait until the email is sent.
Instead, you should use another table to store the action on the row, have a batch job or a service scan the table and send the email itself.
For example:
CREATE TRIGGER SendEmailOnUpdate
ON Author
AFTER UPDATE
AS
INSERT INTO Notification(AuthorId) VALUES(updated.AuthorId);
The create a Windows Service that scan the table Notification and take one row at a time and send an email against the data it contains.
See MSDN for more information about triggers.
You might want to use a Sql Trigger to create a log of all the updates that occur on certain tables and fields.
You may want to use the UPDATE() function (lookup in books on-line) within the trigger to test just for changes to the field you are interested in (i.e. Author)
Actually, using sp_send_dbmail inside a trigger isn't so bad. Unlike the MAPI xp_sendmail days, sp_send_dbmail just queues up the email (essentially writing a record into a table). It will not cause the trigger to wait for the email to be sent. While SQL BOL says the sp "sends an email message", the result of a successful call is the message "Mail queued."
I am still not sure I would call it from a trigger, but I'd now consider it with dbmail.

Web application database concurrency

I have a web application (ASP.net 2.0) that has a database (SQL Server) at the background. I'm considering ways how to handle database concurrency if two users insert the same data to the same table at the same time. Are there any way to handle this case? Thanks in advance.
Jimmy
To prevent the same data being INSERTed at the same time, use a unique index on the columns that you want to be unique. The first INSERT will succeed, handle the error for the losing INSERT appropriately.
To prevent 2+ users from modifying the same (existing) record at the same time, use optimistic concurrency http://en.wikipedia.org/wiki/Optimistic_concurrency_control. With SQL Server it's easy to implement optimistic concurrency using a TIMESTAMP column. Read the timestamp with your data. When updating the row, check that the timestamp value is the same. If the timestamps don't match, then the user had (was viewing) an outdated row. Handle that case appropriately.
An example using SqlClient:
command.CommandText = #"
UPDATE tbl
SET LastName = #LastName, FirstName = #FirstName
WHERE ID = #ID AND Timestamp = #Timestamp
";
int rowCount = command.ExecuteNonQuery();
if (rowCount != 1)
throw new DBConcurrencyException();
All you need is:
BEGIN TRANSACTION;
INSERT XXXXXX INTO TABLE1 ......
INSERT YYYYY INTO TABLE2 ........
COMMIT;
if (commit_failed) { tell user to try again!; };
SQLserver will take care of the rest.
If you are trying to prevent duplicates, the simplest answer is to create a UNIQUE index on the column you want to be unique.
If you are trying to prevent more than one user from modifying the same record at the same time, your best bet is to add a last modified timestamp to the table. As you read the record to display on the screen you read the timestamp, and just before you write the changes you check the timestamp again. If it has changed, that means another user has modified the record and you should prevent the change from being written.
Without knowing how your C# code talks to the database (O/R, ADO.NET...), it's difficult to give you a useful answer...
EDIT: all the examples are great, but they won't help if he's using SubSonic for example.

Resources