Is is safe to use ##IDENTITY in a transaction? - sql

I was reading this answer about different methods of getting the last identity value entered into a database.
From what I understand, ##IDENTITY is usually a very bad idea because it might return an identity that is not the one you expected--for example an identity value that was recently created by a trigger.
But what if your code is in a transaction?
For example this is a simplified version of a transaction I'm doing (using ColdFusion):
<cftransaction>
<cfquery name="queryInsertA" datasource="source">
INSERT INTO tableA (columnName) VALUES (value)
</cfquery>
<cfquery name="queryInsertB" datasource="source">
INSERT INTO tableB (fkey_tableA, columnName) VALUES (##IDENTITY, value)
</cfquery>
</cftransaction>
Since, "If a transaction is successful, all of the data modifications made during the transaction are committed and become a permanent part of the database," does this mean that it would also prevent the isses that can arise when using ##IDENTITY? Or am I misunderstanding the behavior of transactions?

The answer you linked already explains what the main issue is with ##IDENTITY: scope. If your insert triggers another insert, you get an unexpected identity back. Transactions do not change anything.

If you wanted to get the last identity value inserted to a table, use the Ident_current() function.
Select ident_current ('your table name')
Also you can use scope_identity(), It will bring the identity value of a tablein that particular scope only.
Select scope_identity()

You don't need ##Identity, nor do you need 2 separate queries. Use the Scope_identity() function for integrity and make it a part of the same connection & query - like so.
<cfquery name="putUser" datasource="#dsn#">
SET NOCOUNT ON
INSERT INTO users(username, email)
VALUES
('#usersname#','#email#' )
SELECT SCOPE_IDENTITY() AS newId FROM users
SET NOCOUNT OFF
</cfquery>
<cfoutput>#putUser.newID#</cfoutput>
This will be totally safe, but like all db transactions it will still be subject to deadlocks so tuning is still important.
CFTRANSACTION is good for multiple DB operations where some CF logic might also be involved, but let the DB locking and transactional system work for you by keeping it together.

You can also use the result attribute of cfquery. If the query performs an INSERT of an identity or auto-increment value for ID, there will be a key named GENERATEDKEY returned in the structure.
<cftransaction>
<cfquery name="queryInsertA" datasource="source" result="resultA">
INSERT INTO tableA (columnName) VALUES (value)
</cfquery>
<cfquery name="queryInsertB" datasource="source">
INSERT INTO tableB (fkey_tableA, columnName) VALUES (#resultA.generatedKey#, value)
</cfquery>
</cftransaction>
Keep in mind this is only CF9 and higher.

You can use Sequence and use that during insert as below:
CREATE SEQUENCE Testseq
START WITH 1
INCREMENT BY 1 ;
Access the sequence by using below query:
SELECT NEXT VALUE FOR Testseq;

To make it simple :
IF
You know you'r ALL ALONE in the db system, this means, no other user, or process running at the same time, no other transaction running, there is absolutly ZERO activity at the time you use it, and i mean it, ZERO ACTIVITY, then, ok...
ELSE
NO ! If anything like i listed above does occur exactly while your transact is running, you will end up with the wrong identity.

It depends on what else is running at the same time as your transaction is instantiated. If there is a trigger on a table unrelated to the transaction that can insert a new identity value, the transaction scope you are currently in will not protect you.
For example say I create a SPROC that updates Table_A and inserts a record into it. This table has an identity field on it that will increment the ID value in that table each time a new record is inserted. Inside my SPROC I create a transaction and place my insert inside the transaction. After the insert I store the value of ##IDENTITY in a variable inside the same transaction.
Now I also have another table Table_B with it's own identity value but this table is trigger maintained. If I am executing my SPROC to insert a row in Table_A and during this update Table_B is also updated via a trigger, it is possible that when I retrieve the value of ##IDENTITY, it will actually give me the value of the ID created for Table_B rather than Table_A.
You should definitely use Transactions in your stored procedures but you are better off selecting the MAX(ID) of the table you inserted into to retrieve the ID you created rather than ##IDENTITY.

Related

How to insert into the table a user name record

I've a table. In this table I have two columns - 'insert_name' and 'modified_name'. I need to insert into this columns data about who has inserted data into the table('insert_name') and who has changed these data in the table (modified_name). How it can be done?
You are looking for basic DML statements.
If your record is already in the table, then you need to UPDATE it. Otherwise, when you are about to add your record to it and it doesn't already exist in the destination table then you are looking for INSERT INTO statement.
Example of updating information for record with first id:
UPDATE yourtable SET insert_name = 'value1', modified_name = 'value2' WHERE id = 1
Example of inserting new record:
INSERT INTO yourtable(id, company_name, product_name, insert_name)
VALUES (1, 'Google', 'PC', 'value1')
If you are looking for automatic changes to those columns then you need to look into triggers.
Remember that more often than not you may find that the application connecting to the database is using single database user in which case you probably know the context within the application itself (who inserts, who updates). This does eliminate triggers and put the task straight on simple insert/update commands from within your application layer.
You might be able to use the CURRENT_USER function to find the name of the user making the change.
The value from this function could then be used to update the appropriate column. This update could be done as part of the INSERT or UPDATE statement. Alternatively use an INSERT or UPDATE trigger.
Personally I avoid triggers if I can.
For those 2 columns add Current_User as Default constraint.
As the first time Insert Statement will save them with current login user names. For update write an Update trigger with the same Current_User statement for the column Modified_Name.
If and only if your application business logic can't update the column modified_nme then only go for Trigger.
See the use of Current_Use
https://msdn.microsoft.com/en-us/library/ms176050.aspx

Inserting to one table, insert the ID to second table

Is it possible to populate a second table when I insert into the first table?
Insert post to table1 -> table 2 column recieves table1 post's unique id.
What I got so far, am I on the right track?
CONSTRAINT [FK_dbo.Statistics_dbo.News_News_NewsID] FOREIGN KEY ([News_NewsID]) REFERENCES [dbo].[News] ([NewsID])
Lots of ways:
an insert trigger
read SCOPE_IDENTITY() after the first insert, and use it to do a second
use the output clause to do an insert
Examples:
1:
create trigger Foo_Insert on Foo after insert
as
begin
set nocount on
insert Bar(fooid)
select id from inserted
end
go
insert Foo (Name)
values ('abc');
2:
insert Foo (Name)
values ('abc');
declare #id int = SCOPE_IDENTITY();
insert Bar(fooid)
select #id
3:
insert Bar(fooid)
select id from (
insert Foo (Name)
output inserted.id
values ('abc')) x
The only thing I can think of is that you can use a trigger to accomplish this. There is nothing "built in" to SQL Server that would do it. Why not just do it from your .NET code?
Yes it is, it sounds like you want a SQL Trigger, this would allow you to trigger logic based on actions on one table, to perform other actions in the DB. Here's another article on creating Simple SQL Triggers
SQL Server 2008 - Help writing simple INSERT Trigger
A Word of caution, this will do all the logic of updating the new table, outside of any C# code you write, it might sound nice to not have to manage it upfront, but you also lose control over when and if it happens.
So if you need to do something different later, now you have to update your regular code, as well as the trigger code. This type of logic can definitely grow, in large systems, and become a nightmare to maintain. Consider this, the alternative would be to build a method that adds the id to the new table after it inserts into the first table.
While i don't know what you're using to do your inserts assuming it's a SQL Command you can get back the ID on an identity column from the insert using Scope_Identity, found here
How to insert a record and return the newly created ID using a single SqlCommand?
if it's EF or some other ORM tool, they should either automatically update the entity, or have other mechanisms to deliver this data.

Using IDENT_CURRENT() of table during insert into table

I have an existing table that has an identity column and a column that has a constraint to ensure that it matches the value in the identity column.
(I know that this is not a good design, but it is an old table that can not be changed at the moment)
I know that I can use the IDENT_CURRENT('mytable') as a value during the insert to put the newly created identity value into the other column (FakeID).
INSERT INTO MyTable (FakeID)
SELECT IDENT_CURRENT('MyTable')
I have seen that this has issues when inserting multiple records, but for this use I am only inserting a single record.
My question is how safe is this to use?
Mainly, is there a risk from inserts made under different sessions or in a different scope?
Also, does this behave as expected when the table is truncated?
Any other suggestions are welcome.
SCOPE_IDENTITY() is going to be a safer bet here. If someone inserts into the table before this part of your transaction runs, IDENT_CURRENT will grab the newly generated Id from the other transaction.
So I would declare a variable and set its value to SCOPE_IDENTITY(), something like this
DECLARE #Id INT
SET #Id = SCOPE_IDENTITY()
INSERT INTO MyTable (FakeID)
SELECT #Id
Here are some posts on a similar issue:
http://bytes.com/topic/sql-server/answers/471026-ident_current-problem
IDENT_CURRENT equivalent for current session

Stop trigger changing scope_identity

My application inserts some data into a table.
insert into this_Table (id, value, value)
I have then created a trigger that does a simple insert, into a different table with a primary key.
insert into temp_wc_triggertest values ('test', GETDATE())
My problem is then, the application tries to look for scope_identity, from the first insert. However, it gets overwritten by the trigger, which changes the scope identity to the primary key of temp_wc_triggertest.
How can I stop the trigger overwriting scope_identity?
I realise there is not much code to help here, which would normally be classed as a bad question, but I don't have permission to access to the full application code at the moment, so I am hoping this is answerable as is.
This is on SQL Server 2008 R2
EDIT: I have looked at the code, and it does use scope_identity
Your client is most certainly using ##IDENTITY instead of SCOPY_IDENTITY()
Here is a SQL Fiddle with some code you can test on.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table T1(ID int identity(1,1));
create table T2(ID int identity(1000, 1));
go
create trigger tr_T1 on T1 for insert
as
insert into T2 default values;
Query:
insert into T1 default values
select ##identity as "##identity",
scope_identity() as "scope_identity()"
Results:
| ##IDENTITY | SCOPE_IDENTITY() |
---------------------------------
| 1000 | 1 |
If you are using SCOPE_IDENTIY correctly, you may also be experiencing a known bug - http://connect.microsoft.com/SQLServer/feedback/details/328811
MS has fixed it permanently for 2012, and has patches available for 2008 and 2008R2.
The reason for it overwriting scope identity is still not clear, it could possibly be related to the bug mentioned. However a fix was found:
A temporary table was created "temp_wc"
Then at the end of the trigger, identity insert was switched on for that table, and an insert was done, for the ID that we want to keep after the trigger has fired. This method can be thought of as overwriting the overwritten scope identity again.
SET IDENTITY_INSERT ON
INSERT INTO temp_wc VALUES (#ID, 'fix scope identity error')
This may help anyone in the future with this issue:
https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017
You may need to patch SQL Server, as it looks like the SCOPE_IDENTITY should return the inserted ID on the table that is actually receiving the primary insert, not the SQL trigger insert statements.
From the Microsoft docs for SQL Server 2017 (in case link is broken):
Remarks SCOPE_IDENTITY, IDENT_CURRENT, and ##IDENTITY are similar
functions because they return values that are inserted into identity
columns.
IDENT_CURRENT is not limited by scope and session; it is limited to a
specified table. IDENT_CURRENT returns the value generated for a
specific table in any session and any scope. For more information, see
IDENT_CURRENT (Transact-SQL).
SCOPE_IDENTITY and ##IDENTITY return the last identity values that are
generated in any table in the current session. However, SCOPE_IDENTITY
returns values inserted only within the current scope; ##IDENTITY is
not limited to a specific scope.
For example, there are two tables, T1 and T2, and an INSERT trigger is
defined on T1. When a row is inserted to T1, the trigger fires and
inserts a row in T2. This scenario illustrates two scopes: the insert
on T1, and the insert on T2 by the trigger.
Assuming that both T1 and T2 have identity columns, ##IDENTITY and
SCOPE_IDENTITY return different values at the end of an INSERT
statement on T1. ##IDENTITY returns the last identity column value
inserted across any scope in the current session. This is the value
inserted in T2. SCOPE_IDENTITY() returns the IDENTITY value inserted
in T1. This was the last insert that occurred in the same scope. The
SCOPE_IDENTITY() function returns the null value if the function is
invoked before any INSERT statements into an identity column occur in
the scope.
Failed statements and transactions can change the current identity for
a table and create gaps in the identity column values. The identity
value is never rolled back even though the transaction that tried to
insert the value into the table is not committed. For example, if an
INSERT statement fails because of an IGNORE_DUP_KEY violation, the
current identity value for the table is still incremented.
Another link here:
http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/
The problem with this code is that you may not retrieve the identity
value that you inserted. For example, if there is a trigger on the
table performing an insert on another table, you will get the last
created identity value. Even if you never create any trigger, you may
get skewed results with replicated tables since SQL Server creates his
own replication triggers.
Use SELECT IDENT_CURRENT(‘tablename’)
"It returns the last IDENTITY value produced in a table, regardless of the connection that created the value, and regardless of the scope of the statement that produced the value."
See this link for details.
http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/

SQL Server - Return value after INSERT

I'm trying to get a the key-value back after an INSERT-statement.
Example:
I've got a table with the attributes name and id. id is a generated value.
INSERT INTO table (name) VALUES('bob');
Now I want to get the id back in the same step. How is this done?
We're using Microsoft SQL Server 2008.
No need for a separate SELECT...
INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');
This works for non-IDENTITY columns (such as GUIDs) too
Use SCOPE_IDENTITY() to get the new ID value
INSERT INTO table (name) VALUES('bob');
SELECT SCOPE_IDENTITY()
http://msdn.microsoft.com/en-us/library/ms190315.aspx
INSERT INTO files (title) VALUES ('whatever');
SELECT * FROM files WHERE id = SCOPE_IDENTITY();
Is the safest bet since there is a known issue with OUTPUT Clause conflict on tables with triggers. Makes this quite unreliable as even if your table doesn't currently have any triggers - someone adding one down the line will break your application. Time Bomb sort of behaviour.
See msdn article for deeper explanation:
http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx
Entity Framework performs something similar to gbn's answer:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO #generated_keys
VALUES('bob');
SELECT t.[CustomerID]
FROM #generated_keys AS g
JOIN dbo.Customers AS t
ON g.Id = t.CustomerID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, and then selected back to the client. Have to be aware of the gotcha:
inserts can generate more than one row, so the variable can hold more than one row, so you can be returned more than one ID
I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
SQL Server 2008 or newer only. If it's 2005 then you're out of luck.
There are many ways to exit after insert
When you insert data into a table, you can use the OUTPUT clause to
return a copy of the data that’s been inserted into the table. The
OUTPUT clause takes two basic forms: OUTPUT and OUTPUT INTO. Use the
OUTPUT form if you want to return the data to the calling application.
Use the OUTPUT INTO form if you want to return the data to a table or
a table variable.
DECLARE #MyTableVar TABLE (id INT,NAME NVARCHAR(50));
INSERT INTO tableName
(
NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO #MyTableVar
VALUES
(
'test',...
)
IDENT_CURRENT: It returns the last identity created for a particular table or view in any session.
SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]
SCOPE_IDENTITY: It returns the last identity from a same session and the same scope. A scope is a stored procedure/trigger etc.
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
##IDENTITY: It returns the last identity from the same session.
SELECT ##IDENTITY AS [##IDENTITY];
##IDENTITY Is a system function that returns the last-inserted identity value.
There are multiple ways to get the last inserted ID after insert command.
##IDENTITY : It returns the last Identity value generated on a Connection in current session, regardless of Table and the scope of statement that produced the value
SCOPE_IDENTITY(): It returns the last identity value generated by the insert statement in the current scope in the current connection regardless of the table.
IDENT_CURRENT(‘TABLENAME’) : It returns the last identity value generated on the specified table regardless of Any connection, session or scope. IDENT_CURRENT is not limited by scope and session; it is limited to a specified table.
Now it seems more difficult to decide which one will be exact match for my requirement.
I mostly prefer SCOPE_IDENTITY().
If you use select SCOPE_IDENTITY() along with TableName in insert statement, you will get the exact result as per your expectation.
Source : CodoBee
The best and most sure solution is using SCOPE_IDENTITY().
Just you have to get the scope identity after every insert and save it in a variable because you can call two insert in the same scope.
ident_current and ##identity may be they work but they are not safe scope. You can have issues in a big application
declare #duplicataId int
select #duplicataId = (SELECT SCOPE_IDENTITY())
More detail is here Microsoft docs
You can use scope_identity() to select the ID of the row you just inserted into a variable then just select whatever columns you want from that table where the id = the identity you got from scope_identity()
See here for the MSDN info http://msdn.microsoft.com/en-us/library/ms190315.aspx
Recommend to use SCOPE_IDENTITY() to get the new ID value, But NOT use "OUTPUT Inserted.ID"
If the insert statement throw exception, I except it throw it directly. But "OUTPUT Inserted.ID" will return 0, which maybe not as expected.
This is how I use OUTPUT INSERTED, when inserting to a table that uses ID as identity column in SQL Server:
'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)
You can append a select statement to your insert statement.
Integer myInt =
Insert into table1 (FName) values('Fred'); Select Scope_Identity();
This will return a value of the identity when executed scaler.
* Parameter order in the connection string is sometimes important. * The Provider parameter's location can break the recordset cursor after adding a row. We saw this behavior with the SQLOLEDB provider.
After a row is added, the row fields are not available, UNLESS the Provider is specified as the first parameter in the connection string. When the provider is anywhere in the connection string except as the first parameter, the newly inserted row fields are not available. When we moved the the Provider to the first parameter, the row fields magically appeared.
After doing an insert into a table with an identity column, you can reference ##IDENTITY to get the value:
http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx