3

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?

Community
  • 1
  • 1
Ectropy
  • 1,533
  • 4
  • 20
  • 37
  • I would not say that this is 100 % safe, if your doing this in a controled environnement where only you can have access, then yeah, go ahead. But if it's on a system/server that can be used a lot by different process/users/apllications then no. Even if it represent a 1 % risk i would not take it. – Antoine Pelletier Sep 08 '16 at 20:00
  • Why would you want to use @@identity rather than scope_identity()? BTW, the latter is also what CF returns automatically when you using the "result" attribute with simple INSERT's. – Leigh Sep 08 '16 at 21:29
  • I was mainly just curious if this would be a case where `@@identity` is safe. (Looks like that's a big no). `scope_identity()` all the way it is! – Ectropy Sep 09 '16 at 00:22
  • Well, it is definitely better than @@identity. However, if you are running 2008 or earlier with multiple processors, definitely read the parallel plan bug mentioned in the comments of your first link. For 2008, the recommended workaround is using `OUTPUT`. – Leigh Sep 09 '16 at 03:05

7 Answers7

14

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.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • That was my assumption. Good to know that transactions won't help, and you should always just use `select scope_identity()` (or one of the other safer methods of getting the last identity value). – Ectropy Sep 08 '16 at 23:44
2

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()
Unnikrishnan R
  • 4,965
  • 1
  • 12
  • 21
  • 1
    @Ectropy - Since you are looking for the id of the record you just inserted, you definitely want `scope_identity()`, NOT `ident_current()`. As mentioned above, the latter will return the last id for [*any session and any scope*](https://msdn.microsoft.com/en-us/library/ms175098.aspx) - which is not what you want. – Leigh Sep 08 '16 at 23:36
  • Yep, these are the other, better ways of getting identity. – Ectropy Sep 08 '16 at 23:46
  • @Leigh Yeah that sounds right. If someone else inserted into the table that `ident_current()` was looking at I could get an identity value that was not correct. Also not safe! – Ectropy Sep 09 '16 at 00:24
1

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.

Mark A Kruger
  • 7,183
  • 20
  • 21
  • This is useful advice. I've usually done one `cfquery` per `SELECT`/`INSERT`/`UPDATE`/`DELETE` statement. Also the alias for the `SELECT scope_identity()` is useful because it makes it easy to use the identity in another `cfquery`. – Ectropy Sep 09 '16 at 00:32
1

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.

Scott Stroz
  • 7,510
  • 2
  • 21
  • 25
  • Ok, so if you do it the ColdFusion way, via the result's generatedKey there is also no chance the wrong identity value will be selected? Looks like this a good option for people using ColdFusion servers. – Ectropy Sep 09 '16 at 14:23
  • 1
    @Ectropy - Not sure if you saw the earlier comments, but CF's "result" attribute gets the ID by invoking `scope_identity()`. So whatever rules apply to `scope_identity()`, should apply to the result.generatedKey attribute as well. (Note, only applies to single record inserts). – Leigh Sep 09 '16 at 20:50
  • Useful to know. So there are still some cases where OUTPUT may be a better choice. – Ectropy Sep 12 '16 at 18:13
0

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;
Kannan Kandasamy
  • 13,405
  • 3
  • 25
  • 38
  • Interesting, however these particular database tables must use an int identity(1,1) due to certain requirements. Looks like this is advice for making an incrementing value, but not using identity? – Ectropy Sep 08 '16 at 23:40
  • Yes, sequences are "sort of" an alternative to identity. Similar in some senses, but a bit more flexible (albeit a bit Oracle-esque IMO ;-). They were introduced in 2012. [Brief Summary of Sequences](http://stackoverflow.com/a/27392196) and [NEXT VALUE FOR (Transact-SQL)](https://msdn.microsoft.com/en-us/library/ff878370.aspx) – Leigh Sep 09 '16 at 00:23
0

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.

Antoine Pelletier
  • 3,164
  • 3
  • 40
  • 62
  • Not all alone! It's the wild west in the server. Since transactions won't provide any protection, @@IDENTITY isn't safe to use. – Ectropy Sep 08 '16 at 23:51
-2

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.

Rob Hesje
  • 82
  • 6
  • 2
    *you are better off selecting the MAX(ID)* No, do not use MAX. That method is not thread safe either (under the default transaction level). – Leigh Sep 08 '16 at 20:38
  • Agree - it is not a air-tight solution, however I have not yet found a perfect answer to this question when using Identity. Do you have an alternative? – Rob Hesje Sep 09 '16 at 16:04
  • Yes, the ones mentioned in this thread: `scope_identity()` or `OUTPUT` (see note about bug in 2008). While I would not recommend either @@identity or `select max(id)`, the latter is even less safe. At least `@@identity` is limited to the current session. Whereas `select max(id)` is server wide. Under the default transaction level, there is absolutely nothing preventing other threads from obtaining the same value. So on a busy server you are VERY likely to get a completely wrong id. – Leigh Sep 09 '16 at 16:58