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

(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

Related

TableAdapter.insert - Get ID (With SCOPE_IDENTITY)? (SQL Server)

I can't figure out how to get the ID of the record created with tableadapter.insert() command.
I read several answers to similar questions but most of them shift to how to do it with COMMANDS and ADO or with Stored Procedures.
The closest I found was the recommendation to add SELECT SCOPE_IDENTITY() to the end of the INSERT statement.
https://www.screencast.com/t/BU3F73ds
INSERT INTO [zGrid Filters and Layouts]
(XML, UserID, Type, Global, LastModDt, SaveName)
VALUES (#XML,#UserID,#Type,#Global,#LastModDt,#SaveName);
SELECT SCOPE_IDENTITY()
But when I do that, I get a "can't parse" error when I click Finish.
https://www.screencast.com/t/7R5MN3cF
Then I also set the Execute to Scalar.
https://www.screencast.com/t/w91RECeLUL2
And if I bypass that error, I get a runtime error saying I need to declare the scalar value.
https://www.screencast.com/t/RbNuizo65vu
I've verified my INSERT (SQL) code. In fact, I've confirmed it does insert without the SELECT SCOPE_IDENTITY line.
Dim ID As Int32 =
DataTableGridFiltersLayoutsTableAdapter1.InsertRecord(xmlString,
1, "F", 1, Now, "My New Block 3")
I included the results, above.
To clarify, I want to figure this out using the generated table adapter -- unless the answer is you can't do it with table adapters (which doesn't seem right).
I've found countless articles that say it's possible but none of them actually show you how to do it -- beyond the tableadapter.insert line.
Any help is appreciated.

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

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

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.

DataTable identity column not set after DataAdapter.Update/Refresh on table with "instead of"-trigger (SqlServer 2005)

Within our unit tests we use plain ADO.NET (DataTable, DataAdapter) for preparing the database resp. checking the results, while the tested components themselves run under NHibernate 2.1. .NET version is 3.5, SqlServer version is 2005.
The database tables have identity columns as primary keys. Some tables apply instead-of-insert/update triggers (this is due to backward compatibility, nothing I can change). The triggers generally work like this:
create trigger dbo.emp_insert
on dbo.emp
instead of insert
as
begin
set nocount on
insert into emp ...
select ##identity
end
The insert statement issued by the ADO.NET DataAdapter (generated on-the-fly by a thin ADO.NET wrapper) tries to retrieve the identity value back into the DataRow:
exec sp_executesql N'
insert into emp (...) values (...);
select id, ... from emp where id = ##identity
'
But the DataRow's id-Column is still 0. When I remove the trigger temporarily, it works fine - the id-Column then holds the identity value set by the database.
NHibernate on the other hand uses this kind of insert statement:
exec sp_executesql N'
insert into emp (...) values (...);
select scope_identity()
'
This works, the NHibernate POCO has its id property correctly set right after flushing. Which seems a little bit counter-intuitive to me, as I expected the trigger to run in a different scope, hence ##identity should be a better fit than scope_identity().
So I thought no problem, I will apply scope_identity() instead of ##identity under ADO.NET as well. But this has no effect, the DataRow value is still not updated accordingly.
And now for the best part: When I copy and paste those two statements from SqlServer profiler into a Management Studio query (that is including "exec sp_executesql"), and run them there, the results seem to be inverse! There the ADO.NET version works, and the NHibernate version doesn't (select scope_identity() returns null). I tried several times to verify, but to no avail. Well, actually that was what I would have expected - ##identity to be OK, and scope_identity() to fail.
Of course invoking it in Management Studio just shows the resultset coming from the database, whatever happens inside NHibernate and ADO.NET is another topic. Also, several session properties defined by T-SQL SET are different in the two scenarios (Management Studio query vs. application at runtime)
This is a real puzzle to me. I would be happy about any insights on that. Thank you!
Found it. The identity value actually was transmitted into the DataTable, just not in the column I expected. Instead of using existing column "id", ADO.NET created a new column "Column1".
Reason is this line at the end of the instead-of trigger:
select ##identity
Unfortunately, NHibernate seems to require "select ##identity" at the end of instead-of triggers (this was mentioned in a Hibernate forum posting, and I verified it again now - it is indeed necessary). But I can go along from here (adapting NHibernate dialect is one possibility)...