Get last UNIQUEIDENTIFIER inserted in a table inside a TRIGGER from a SQL 2005 database - sql-server-2005

I need to be able to monitor a Table and react very time a record is inserted. This table has no ITN IDENTITY field, only a UNIQUEIDENTIFIER as its primary key. Without any alteration of existing inputs, SPs, etc. I need to be able to find the last inserted ID from within a trigger. This is what I have (obviously does not work):
CREATE TRIGGER TR_UserInserted
ON Users
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
EXEC UserInserted (SELECT User_Id FROM INSERTED);
END
GO
Here I am trying to get the User_Id from the last inserted record in the Users table and run it through the UserInserted SP. Thank you for the help, I am stumped.
HLGEM Made a great point - even on a bulk insert, I only need the last record inserted - I know this is a strange request.

You need to change your trigger to fire INSTEAD OF INSERT. A uniqueidentifier variable must be generated using the NEWID() function. In the INSERT statement in the trigger body, the columns must be provided in order. Assuming a table defined this way:
CREATE TABLE Users (
First int,
User_Id uniqueidentifier PRIMARY KEY,
Third int,
Fourth int)
Then the trigger is:
CREATE TRIGGER TR_UserInserted ON Users
INSTEAD OF INSERT AS
BEGIN
DECLARE #newid uniqueidentifier = NEWID()
INSERT INTO Users
SELECT
First,
#newid,
Third,
Fourth
FROM inserted
EXECUTE UserInserted(#newid)
-- you can actually provide all the columns to UserInserted
END
For this to work properly, make sure that the table does not have a default for the primary key as NEWID().

Related

SQL Server triggers - insert new rows into audit table

I have a simple table as below.
I want to create a trigger to insert new values into "SectionsAudit" Table.
Means:
If a new row is inserted into the Sections table, I want to insert the same row into the Audit table
If an existing row is updated in the Sections table, I want to create a new row in the Audit table with the updated row.
How can I do that in SQL Server? Also, I would like to know if this a good practice?
CREATE TABLE [dbo].[Sections]
(
[Id] int IDENTITY(1,1) NOT NULL,
[Name] varchar(25) NOT NULL,
[InsertedBy] varchar(25) NOT NULL,
[InsertedDateTime] datetime NOT NULL,
[UpdatedBy] varchar(25) NOT NULL,
[UpdatedDateTime] datetime NOT NULL,
CONSTRAINT [PK_Id] PRIMARY KEY CLUSTERED([Id])
)
The trigger will look something like this, its a good practice depending of the MS SQL version you're using.
Note that for using this approach is necessary to have in your dbo.SectionsAudit a field to track the ModificationId of the original Record (in the example I called it IdModi)
CREATE TRIGGER [dbo].[tr_SectionsAudit]
ON [dbo].[Sections]
AFTER INSERT, UPDATE
NOT FOR REPLICATION
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT SectionsAudit
(Id, IdModi, fieldname, ...)
SELECT Id, IdModi = (ISNULL((SELECT ISNULL(MAX(SA.IdModi), 0)
FROM SectionsAudit SA
WHERE SA.Id = I.Id
) , 0) + 1),
fieldname, ...
FROM INSERTED I
END
In your case I suggest you to use Rowversion or Change Data Capture.
Below some option to do it:
Rowversion
It is a unique binary numbers of 8 bytes. It is updated every time that you insert or update any column of any records
Example:
CREATE TABLE dbo.Test (ID int PRIMARY KEY, RowVersion rowversion) ;
Change Data Capture
Change data capture records insert, update, and delete activity that is applied to a SQL Server table. This makes the details of the changes available in an easily consumed relational format. Column information and the metadata that is required to apply the changes to a target environment is captured for the modified rows and stored in change tables that mirror the column structure of the tracked source tables. Table-valued functions are provided to allow systematic access to the change data by consumers.
Temporal Tables
Start from SQLServer 2016 you can use the system-versioned temporal table that is a type of user table designed to keep a full history of data changes and allow easy point in time analysis. This type of temporal table is referred to as a system-versioned temporal table because the period of validity for each row is managed by the system (i.e. database engine).
Every temporal table has two explicitly defined columns, each with a datetime2 data type. These columns are referred to as period columns. These period columns are used exclusively by the system to record period of validity for each row whenever a row is modified.
Example:
CREATE TABLE dbo.test
(
[ID] int NOT NULL PRIMARY KEY CLUSTERED
,[ValidFrom] datetime2 GENERATED ALWAYS AS ROW START
,[ValidTo] datetime2 GENERATED ALWAYS AS ROW END
,PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo))
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TestHistory));
Triggers
Creates a DML, DDL, or logon trigger. A trigger is a special type of stored procedure that automatically runs when an event occurs in the database server. DML triggers run when a user tries to modify data through a data manipulation language (DML) event. DML events are INSERT, UPDATE, or DELETE statements on a table or view. These triggers fire when any valid event fires, whether table rows are affected or not.

SQL Server: how to insert a record into related table during an update?

Let's say we have two tables, Incidents and IncidentTracking:
Incidents (Id INT PRIMARY KEY,
CreatedOn Datetime,
State VARCHAR(50))
IncidentTracking (Id INT PRIMARY KEY,
IncidentId INT FOREIGN KEY REFERENCES TO Incidents.Id,
TrackingDate Datetime,
NewState VARCHAR(50))
How do I insert a new record into IncidentTracking while updating some incidents?
For example, I want to change the State for all incidents that are more than 90 days old to "Outdated", and insert a tracking info record where IncidentId is the updated incident (SCOPE_IDENTITY() maybe?), TrackingDate is GETDATE() and NewState is also "Outdated".
Can it be done all in one statement or should I write a cursor?
I'd use OUTPUT clause.
As IncidentTracking has a foreign key, it is not possible to OUTPUT directly to it. You'll get an error message if you try:
The target table 'dbo.IncidentTracking' of the OUTPUT INTO clause
cannot be on either side of a (primary key, foreign key) relationship.
Found reference constraint 'FK_IncidentTracking_Incidents'.
So, we can use a temporary table or table variable.
Like this:
BEGIN TRANSACTION;
DECLARE #T TABLE (IncidentId int, TrackingDate datetime, NewState varchar(50));
UPDATE [dbo].[Incidents]
SET [State] = 'Outdated'
OUTPUT
inserted.Id AS [IncidentId],
GETDATE() AS [TrackingDate],
inserted.[State] AS [NewState]
INTO #T ([IncidentId], [TrackingDate], [NewState])
WHERE [CreatedOn] < DATEADD(day, -90, GETDATE())
;
INSERT INTO [dbo].[IncidentTracking] ([IncidentId], [TrackingDate], [NewState])
SELECT [IncidentId], [TrackingDate], [NewState]
FROM #T;
COMMIT TRANSACTION;
Write a stored procedure to perform your task. You can put that kind of logic in a stored procedure easily enough.
If you will allow access to the table(s) outside of the procedure and still want the same behavior, a trigger is likely what you want (not a fan of them myself). Make sure, when writing your trigger, you remember that it will run against a recordset not a single record.
You could have a trigger on the state field that inserts into IncidentTracking, then you just need the one update. Or the other way around (adding to IncidentTracking updates State)
But why even have the State in the Incidents table? Just have an IncidentState table where you add every state change, and the latest added one is the current state for the incident.
You'd also probably want to make the State field a StateId column instead of a text code. Connected to a State table containing all the states.

Update identity column sql [duplicate]

I have a MS SQL 2005 database with a table Test with column ID. ID is an identity column.
I have rows in this table and all of them have their corresponding ID auto incremented value.
Now I would like to change every ID in this table like this:
ID = ID + 1
But when I do this I get an error:
Cannot update identity column 'ID'.
I've tried this:
ALTER TABLE Test NOCHECK CONSTRAINT ALL
set identity_insert ID ON
But this does not solve the problem.
I need to have identity set to this column, but I need to change values as well from time to time. So my question is how to accomplish this task.
You need to
set identity_insert YourTable ON
Then delete your row and reinsert it with different identity.
Once you have done the insert don't forget to turn identity_insert off
set identity_insert YourTable OFF
IDENTITY column values are immutable.
However it is possible to switch the table metadata to remove the IDENTITY property, do the update, then switch back.
Assuming the following structure
CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)
INSERT INTO Test
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'
Then you can do
/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)
/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;
/*Do the update*/
UPDATE Temp SET ID = ID + 1;
/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;
/*ID values have been updated*/
SELECT *
FROM Test
/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
DROP TABLE Temp /*Drop obsolete table*/
In SQL Server 2012 it is possible to have an auto incrementing column that can also be updated more straightforwardly with SEQUENCES
CREATE SEQUENCE Seq
AS INT
START WITH 1
INCREMENT BY 1
CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)
INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'
UPDATE Test2 SET ID+=1
Through the UI in SQL Server 2005 manager, change the column remove the autonumber (identity) property of the column (select the table by right clicking on it and choose "Design").
Then run your query:
UPDATE table SET Id = Id + 1
Then go and add the autonumber property back to the column.
Firstly the setting of IDENTITY_INSERT on or off for that matter will not work for what you require (it is used for inserting new values, such as plugging gaps).
Doing the operation through the GUI just creates a temporary table, copies all the data across to a new table without an identity field, and renames the table.
This can be done using a temporary table.
The idea
disable constraints (in case your id is referenced by a foreign key)
create a temp table with the new id
delete the table content
copy back data from the copied table to your original table
enable previsously disabled constraints
SQL Queries
Let's say your test table have two additional columns (column2 and column3) and that there are 2 tables having foreign keys referencing test called foreign_table1 and foreign_table2 (because real life issues are never simple).
alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;
select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy
alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;
That's it.
DBCC CHECKIDENT ( ‘databasename.dbo.orders’,RESEED, 999)
you can change any identity column number with this command,and also you can start that field number from every number you want.for example in my command i ask to start from 1000 (999+1)
hope that it would be enough...good luck
If the column is not a PK you could always create a NEW column in the table with the incremented numbers, drop the original and then alter the new one to be the old.
curious as to why you might need to do this... most I've ever had to futz with Identity columns was to backfill numbers and I just ended up using DBCC CHECKIDENT ( tablename,RESEED,newnextnumber)
good luck!
Identity modifying may fail depending on a number of factors, mainly revolving around the objects/relationships linked to the id column. It seems like db design is as issue here as id's should rarely if ever change (i'm sure you have your reasons and are cascasding the changes). If you really need to change id's from time to time, I'd suggest either creating a new dummy id column that isn't the primary key/autonumber that you can manage yourself and generate from the current values. Alternately, Chrisotphers idea above would be my other suggestion if you're having issues with allowing identity insert.
Good luck
PS it's not failing because the sequential order it's running in is trying to update a value in the list to an item that already exists in the list of ids? clutching at straws, perhaps add the number of rows+1, then if that works subtract the number of rows :-S
If you need to change the IDs occasionally, it's probably best not to use an identity column. In the past we've implemented autonumber fields manually using a 'Counters' table that tracks the next ID for each table. IIRC we did this because identity columns were causing database corruption in SQL2000 but being able to change IDs was occasionally useful for testing.
You can insert new rows with modified values and then delete old rows. Following example change ID to be same as foreign key PersonId
SET IDENTITY_INSERT [PersonApiLogin] ON
INSERT INTO [PersonApiLogin](
[Id]
,[PersonId]
,[ApiId]
,[Hash]
,[Password]
,[SoftwareKey]
,[LoggedIn]
,[LastAccess])
SELECT [PersonId]
,[PersonId]
,[ApiId]
,[Hash]
,[Password]
,[SoftwareKey]
,[LoggedIn]
,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO
DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
First save all IDs and alter them programmatically to the values you wan't, then remove them from database and then insert them again using something similar:
use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
([Id])
VALUES
(2)
set identity_insert [Test] OFF
For bulk insert use:
use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n',
KEEPIDENTITY)
set identity_insert [Test] OFF
Sample data from file.csv:
2;
3;
4;
5;
6;
If you don't set identity_insert to off you will get the following error:
Cannot insert explicit value for identity column in table 'Test' when
IDENTITY_INSERT is set to OFF.
I saw a good article which helped me out at the last moment .. I was trying to insert few rows in a table which had identity column but did it wrongly and have to delete back. Once I deleted the rows then my identity column got changed . I was trying to find an way to update the column which was inserted but - no luck. So, while searching on google found a link ..
Deleted the columns which was wrongly inserted
Use force insert using identity on/off (explained below)
http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do-i-update-the-value-of-an.aspx
Very nice question, first we need to on the IDENTITY_INSERT for the specific table, after that run the insert query (Must specify the column name).
Note: After edit the the identity column, don't forget to off the IDENTITY_INSERT. If you not done, you cannot able to Edit the identity column for any other table.
SET IDENTITY_INSERT Emp_tb_gb_Menu ON
INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF
http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Inserting row with foreign key relation in same transaction as primary row

I have a 2 tables where one has a foreign key relation to the other
CREATE TABLE foo (
id INT NOT NULL PRIMARY KEY IDENTITY,
value VARCHAR(50) DEFAULT NULL,
);
CREATE TABLE bar (
id INT NOT NULL PRIMARY KEY IDENTITY,
foo_key INT NOT NULL
value VARCHAR(50) DEFAULT NULL,
);
I'm using parameterized ADO.NET ExecuteReader to Insert new rows. My pickle is, if I want to insert 2 rows in different tables in the same transaction, i.e. before commit, I cannot insert rows in bar since I don't know the value that has been given foo.id yet. How would you go about doing that? i.e. How do I make sure that bar.foo_key get assigned the right value? Trying to select on it brings nothing, since I guess it is not actually there yet. Should I use a stored procedure to try and generate the key on the fly, or maybe there is an internal variable that can be used. Or is there a way to have the insert return the new id? Do I need a foreign key declaration, though I'm not sure that would be useful since again I still don't know what id to use?
The reason why I want to do it in one go, is due to error handling, I want to be able to roll everything back in case of an error.
You can use scope_identity() to retrieve the newly generated identity:
begin tran;
insert Foo (value) values ('6*7');
declare #fk int = scope_identity();
insert bar (foo_key, value) values (#fk, '42');
commit tran;
Per HLGEM's comment, to return the value of the newly generated identity to the client, you can use output:
insert Foo (value) output inserted.ID values ('6*7');
Note that for a transaction to span two sessions, you need a distributed transaction, which is very expensive.
I figured out I can return scope_identity() on the insert
INSERT INTO [foo] ([value]) VALUES (#0) SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];

Trigger for insert on identity column

I have a table A with an Identity Column which is the primary key.
The primary key is at the same time a foreign key that points towards another table B.
I am trying to build an insert trigger that inserts into Table B the identity column that is about to be created in table A and another custom value for example '1'.
I tried using ##Identity but I keep getting a foreign key conflict. Thanks for your help.
create TRIGGER dbo.tr ON dbo.TableA FOR INSERT
AS
SET NOCOUNT ON
begin
insert into TableB
select ##identity, 1;
end
alexolb answered the question himself in the comments above. Another alternative is to use the IDENT_CURRENT function instead of selecting from the table. The drawback of this approach is that it always starts your number one higher than the seed, but that is easily remedied by setting the seed one unit lower. I think it feels better to use a function than a subquery.
For example:
CREATE TABLE [tbl_TiggeredTable](
[id] [int] identity(0,1) NOT NULL,
[other] [varchar](max)
)
CREATE TRIGGER [trgMyTrigger]
ON [tbl_TriggeredTable]
INSTEAD OF INSERT,UPDATE,DELETE
SET identity_insert tbl_TriggeredTable ON
INSERT INTO tbl_TriggeredTable (
[id],
[other]
)
SELECT
-- The identity column will have a zero in the insert table when
-- it has not been populated yet, so we need to figure it out manually
case i.[id]
when 0 then IDENT_CURRENT('tbl_TriggeredTable') + IDENT_INCR('tbl_TriggeredTable')
ELSE i.[id]
END,
i.[other],
FROM inserted i
SET identity_insert tbl_TriggeredTable OFF
END