Do ##Identity And Scope_Identity Always Return The Record Just Added? - sql

I understand the differences between ##IDENTITY and SCOPE_IDENTITY, I think, but I'm struggling to find out exactly how they're generated.
All the documentation tells me that these functions return the ID of the last record added to a table, but if I have a Stored Procedure containing an INSERT statement, and that procedure is part of a heavily-used database that could be getting executed by multiple users at the same time, if those two users both insert a record into the same table fractions of a second apart, is it possible that if I call ##IDENTITY or SCOPE_IDENTITY from the Stored Procedure right after the INSERT statement, they could actually return the ID of a record inserted by a different user?
I think the answer is that SCOPE_IDENTITY would avoid this because, as the name suggests, it gets the identity of the last record added from within the scope of the call to SCOPE_IDENTITY (in this case, from within the same Stored Procedure), but since I'm not entirely sure what the definition of the scope is, I don't know if I'm right in thinking this.

Both ##identity and scope_identity() will return the id of a record created by the same user.
The ##identity function returns the id created in the same session. The session is the database connection, so that is normally the same thing as the user.
The scope_identity() function returns the id created in the same session and the same scope. The scope is the current query or the current stored procedure.
So, the difference between them is if you for example call a procedure from another procedure; if the called procedure inserts a record, using ##identity after the call will return that id, but scope_identity() will not.

Way back before I knew better I ran a query like
select max(id) from table
to get the ID of a record I just inserted. When you use something like this in a production environment where you have multiple users adding records concurrently, bad things happen.
You are implying that ##Identity and scope_identity() work the same way as the query above. That's not the case. They both return the value of identity columns generate via inserts WITHIN THE CURRENT USER'S SESSION ONLY!. Scope_Identity() is useful if you have tables that have triggers on them and the trigger logic does it's own inserts. In those cases ##Identity would return the identity value generated within the trigger, which is probably not what you want. It's for that reason I almost always prefer Scope_Identity() to ##Identity

Related

Retrieving ids of batch inserted rows in SQLite

I'm using SQLite's last_insert_rowid() to grab the last inserted row ID following a batch insert. Is there any risk of race conditions that could cause this value to not return the last id of the batch insert? For example, is it possible that in between the completion of the insert and the calling of last_insert_rowid() some other process may have written to the table again?
last_insert_rowid() is connection-dependent, so there is a risk when multiple threads are using the same connection, without SQLite switched to Serialized threading mode.
last_insert_rowid() returns information about the last insert done in this specific connection; it cannot return a value written by some other process.
To ensure that the returned value corresponds to the current state of the database, take advantage of SQLite's ACID guarantees (here: atomicity): wrap the batch inserts, the last_insert_rowid() call, and whatever you're doing with the ID inside a single transaction.
In any case, the return value of last_insert_rowid() changes only when some insert is done through this connection, so you should never access the same connection from multiple threads, or if you really want to do so, manually serialize entire transactions.

Oracle SQL and autonumbering: how to retrieve an OUT parameter - outside the procedure

On an earlier stage, our system was provided with tables that hold last used autonumber (instead of using sequences). We are now redoing the client solution for the system, and need to 'reinvent' how to fetch next record number - by SQL.
The client application is made in FileMaker, the database still resides in Oracle. The challenge is to update last used autonumber AND supply it to the new record initiated in the client - in one operation.
A SELECT statement can retrieve the last used number.
An UPDATE statement can increment the last used number.
A function selecting and returning the number is not allowed to contain update statements.
A procedure may do the update, and may retain the new value returning it into an OUT parameter, but does not return the new value to the client - unless the client in some way can read the OUT parameter from the procedure (I do not think it reads DBMS_OUTPUT).
If the procedure proceeds to do an INSERT on the table where the client is preparing an INSERT, the inserts will not be identical, as far as I can see.
So - is there a syntax that will make the OUT value accessible to the client as result of an SQL statement including a procedure call (perhaps making the OUT parameter in some way refer to the client's new record recnr field), or is this altogether a blind alley?
regarding syntax - You need to wrap your PL/SQL procedure with out param into function (you can use overloaded function with the same name in the same package) and return out value as function result.
Regarding design - I do not recommend to use "home-made" mechanism to replace sequences. Sequences are much better optimised and more reliable solution.

SQL Server stored procedure locking issue?

I created this pretty basic stored procedure, that gets called by our cms when a user creates a specific type of item. However, it looks like there are times when we get two rows for each cms item created with the same data, but an off-by-one SourceID. I don't do much SQL work, so this might be something basic - but do I need to explicitly lock the table somehow in the stored procedure to keep this from happening?
Here is the stored procedure code:
BEGIN
SET #newid = (SELECT MAX(SourceID)+1 from [dbo].[sourcecode])
IF NOT EXISTS(SELECT SourceId from [dbo].[sourcecode] where SourceId = #newid)
INSERT INTO [dbo].[sourcecode]
(
SourceID,
Description,
RunCounts,
ShowOnReport,
SourceParentID,
ApprovedSource,
Created
)
VALUES
(
#newid,
#Desc,
1,
#ShowOnReport,
1,
1,
GetDate()
)
RETURN #newid
END
and here is an example of the duplicated data (less a couple of irrelevant columns):
SourceId Description Created
676 some text 2012-10-17 09:42:36.553
677 some text 2012-10-17 09:43:01.380
I am sure this has nothing to do with SP. As Oded mentioned, this could be the result of your code.
I don't see anything in the stored procedure which is capable of generating duplicates.
Also, I wouldn't use MAX(SourceId) + 1. Why don't you use "Auto Increment" if you want a new Source Id all the time anyways?
As it has been said in the comments, I think your issue is more in the code layer; none of the data seems to be violating any constraints. You may want to do a check to see if the same user has submitted the same data "recently" before performing the insert.
You can use locking when using stored procedures. On the ones I use I usually use WITH (ROWLOCK). Locking is used to ensure data integrity. I think a simple Google should bring up lots of information about why you should be using locking.
But as other commentators had said, see if there isn't anything in your code as well. Is there something that is calling the same method twice? Are there 'events' referencing the method that is doing the updating?
The description is probably duplicated because you are calling the same function twice, by clicking the button twice, or whatever.
You should use an IDENTITY on your SourceID column and use the Scope_Identity() function
If you don't want to do that for some reason, then you should wrap the above code in a transaction with the isolation level set to Serializable
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SET #newid = ....
COMMIT

Does ##IDENTITY in an SP using triggers result in a race condition?

If I were to use ##IDENTITY in a SQL sp which also activated a trigger, would it result in a race condition?
Say I perform an INSERT INTO TABLE_A which triggers an INSERT INTO TABLE_B then, in the current scope I use ##IDENTITY
Would I always get the resulting identity from the trigger (TABLE_B) or would it depend on which thread finished first?
Note: I know about SCOPE_IDENTITY, this question is hypothetical....
I believe you'll always get the identity from table B, because your transaction in Table A isn't considered complete and committed (and your T-SQL isn't allowed to advance) until the insert and trigger are both complete. Even if you're using an AFTER trigger, you're still waiting until the trigger code is done before you get to the next line and ask for the ##identity.

Why would ##IDENTITY or SCOPE_IDENTITY() be DBNull?

(resolved: see bottom)
I have the following code snippet:
Protected Sub SqlDataSource1_Inserted(ByVal sender As Object,
ByVal e As System.Web.UI.WebControls.SqlDataSourceStatusEventArgs)
Handles SqlDataSource1.Inserted
affected = CInt(DirectCast(e.Command.Parameters("#affected"), IDbDataParameter).Value)
newID = CInt(DirectCast(e.Command.Parameters("#newID"), IDbDataParameter).Value)
End Sub
Where #newID is defined like this in the SQL string:
"INSERT INTO x(a,b,c) VALUES (#a,#b,#c); SELECT #affected = ##rowcount, #newID = SCOPE_IDENTITY();
The parameters are defined using ASP.NET as follows:
The strange thing about it is that this works 90% of the time, but every once and a while it throws an InvalidCastException saying that "Conversion from type 'DBNull' to type 'Integer' is not valid." Any ideas on what could be causing this value to be null? I don't have any triggers set on the table, and the only thing my query is doing is running a plain insert into 1 table.
Edit: Based on the suggestions here, I added an affected parameter. I set a breakpoint, and affected = 1 but I still got the exception. However, I then figured out that I had SELECT #newID before SELECT #affected. I switched the order, and now #affected = 0. So it appears to be a problem with my insert statement after all. Thanks for your help!
From MSDN
After an INSERT, SELECT INTO, or bulk
copy statement is completed,
##IDENTITY contains the last identity
value that is generated by the
statement. If the statement did not
affect any tables with identity
columns, ##IDENTITY returns NULL.
Do you have a check to see if the insert has succeeded? It may be possible that the record you are trying to insert fails to insert anything for some reason, therefore the id is null.
Are there any triggers set up on the table? You may be retrieving the id from a trigger procedure using ##IDENTITY
I would suggest either using SCOPE_IDENTITY() or an output parameter to get the id
You should use SCOPE_IDENTITY() as oppose to ##IDENTITY in 99.9% of cases. It is very rare you will have a situation that requires something other than SCOPE_IDENTITY()
##IDENTITY returns the last IDENTITY
value produced on a connection,
regardless of the table that produced
the value, and regardless of the scope
of the statement that produced the
value. If you have a trigger on a
table that causes an identity to be
created in another table, you will get
the identity that was created last,
even if it was the trigger that
created it.
SCOPE_IDENTITY() returns the last IDENTITY value produced on a
connection and by a statement in the
same scope, regardless of the table
that produced the value.
SCOPE_IDENTITY(), like ##IDENTITY,
will return the last identity value
created in the current session, but it
will also limit it to your current
scope as well.
Are you sure there are no auditing triggers on that table (perhaps added by your DBA)?
Various possibilities:
INSERT does not insert anything (as
erikkallen said)
your statement (not shown) inserts
to various tables, the last one of
which does not have an identity
column
the INSERT fires a trigger which
inserts to a table without an
identity column - try
SCOPE_IDENTITY() instead
You might want to perform a check to see if ##ROWCOUNT > 0.
Also there were known problems in past versions of SQL Server with triggers impacting the survivability of ##IDENTITY. Do you have any triggers?
Another time when this occurred, changing
<asp:Parameter Name="newID" Direction="Output" Size="4" />
to
<asp:Parameter Name="newID" Direction="Output" Type="Int32" Size="4" />
fixed the problem for me. It appeared that my host turned on some requirements for more explicit declaration.
What is the query text... part? An INSERT, I guess.
Could it be an insert ... select which didn't find any rows?
You may receive incorrect values when using SCOPE_IDENTITY() and ##IDENTITY. Specifically when the query ends up being executed as a parallel query:
http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779