2

My code does not update the thread field. It is null. Anyone have any ideas?

INSERT INTO [Messages]([Sender], [Receiver], [Job_Number], [Subject], [MessageText], [DateSent])
VALUES(@Sender, @Receiver, @Job_Number, @Subject, @MessageText, @DateSent)

SET @ThreadID = SCOPE_IDENTITY()

UPDATE [Messages] 
SET Thread = @ThreadID
WHERE MessageID = @ThreadID

EDIT: It seems the UPDATE routine isn't being executed at all. I even added the following code to the end of the sproc, but nothing gets updated.

UPDATE Comments 
SET SomeField = @ThreadID
where SCID = 33

EDIT:

/****** Object:  Table [dbo].[Messages]    Script Date: 04/09/2010 12:08:55 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Messages](
    [MessageID] [int] IDENTITY(1,1) NOT NULL,
    [Sender] [varchar](30) NOT NULL,
    [Receiver] [varchar](30) NOT NULL,
    [Job_Number] [varchar](20) NULL,
    [Subject] [varchar](200) NULL,
    [MessageText] [varchar](max) NULL,
    [DateSent] [datetime] NULL,
    [Thread] [int] NULL,
 CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED 
(
    [MessageID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[Messages] ADD  CONSTRAINT [DF_Messages_DateSent]  DEFAULT (getdate()) FOR [DateSent]
GO

EDIT: When I execute the stored procedure from Management Studio, the update works just fine. The problem is in my app when I call it using SQLHelper:

SqlHelper.ExecuteNonQuery(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString,
            "spMessagesInsert",
            0,
            message.Sender, 
            message.Receiver,
            message.Job_Number,
            message.Subject,
            message.MessageText,
            message.DateSent
            );

EDIT: Ultimately, I changed the program code to call the stored procedure using Linq-to-sql instead of using SqlHelper. This seemed to fix the issue.

jinsungy
  • 10,717
  • 24
  • 71
  • 79
  • 3
    Are you sure there is an identity column specified on the Messages table? – Daniel DiPaolo Apr 09 '10 at 15:49
  • Then are you sure the INSERT is succeeding? You may want to check for errors or check `@@ROWCOUNT` to see if it's actually inserting anything that would update `SCOPE_IDENTITY` – Daniel DiPaolo Apr 09 '10 at 15:52
  • There are no triggers in play whatsoever and the Insert is succeeding. I see the row being inserted. – jinsungy Apr 09 '10 at 15:55
  • 2
    More specifically, is MessageID the identity column? At the moment it looks like you're copying the new ID to another column (Thread) in the same row, which seems pointless. – MartW Apr 09 '10 at 16:04
  • 1
    Yes, MessageID is the Identity column. I do this to group messages - it is not pointless. – jinsungy Apr 09 '10 at 16:06
  • 1
    Can you post your exact scripted DDL for the table? – Martin Smith Apr 09 '10 at 16:07
  • But how can it group messages when it's based on the table's unique ID column and therefore unique for each message? +1 for the DDL – MartW Apr 09 '10 at 16:09
  • What do you get if you put "Select @ThreadId" right after you set it to `SCOPE_IDENTITY`? If you get a value, what do you get if you query for it (i.e. Select * From Messages Where MessageId = @ThreadId). – Thomas Apr 09 '10 at 16:13
  • 1
    @CodeByMoonlight - I use it to group messages when several rows have the same ThreadID (which is really the messageID of the first row). Please stop asking about the validity of it as it is besides the point. – jinsungy Apr 09 '10 at 16:16
  • Do the declarations for @Sender, @Receiver, @Job_Number, @Subject, @MessageText and @DateSent match those for the table structure? It now looks like possibly an error in the INSERT is causing the rest to not run. 30 characters seems quite short for Sender and Receiver – MartW Apr 09 '10 at 16:18
  • Ah, now I get the grouping :) – MartW Apr 09 '10 at 16:19
  • The Declarations all match. The insert statement executes successfully each time. 30 char max is fine - no truncating of data is happening. – jinsungy Apr 09 '10 at 16:21
  • 1
    Is the first part all run within one stored procedure? Does it begin with SET NoCount On? – MartW Apr 09 '10 at 16:22
  • The plot thickens. Ok, so the SQL code is function properly. Is your call to ExecuteNonQuery wrapped in a transaction or TransactionScope that is getting rolled back? – Thomas Apr 09 '10 at 16:36
  • Hmm, Clutching at straws a bit here but there couldn't be another copy of spMessagesInsert in a different schema than dbo? – Martin Smith Apr 09 '10 at 16:38
  • @Thomas - no transactioning. I wish it was that easy. – jinsungy Apr 09 '10 at 16:38
  • 1
    @jinsungy - Try running SQL Profiler. It will show you whether something else is updating that record or if a transaction is being rolled back (include Rollback Tran completed). – Thomas Apr 09 '10 at 16:42
  • @Martin - I am positive there is no other copy of the sproc elsewhere. – jinsungy Apr 09 '10 at 16:42
  • I second Thomas's profiler suggestion. Hopefully something will become clear when you look at the trace. – Martin Smith Apr 09 '10 at 16:47
  • @Thomas - running a trace now. Yes, when I run my code using SqlHelper, the record gets inserted successfully but the Thread column is null. – jinsungy Apr 09 '10 at 16:51
  • @jinsungy - If that is the case, then what you are probably looking for is something else writing a null into that column. – Thomas Apr 09 '10 at 16:54
  • no transaction, no rollback, no updating of the field to null, no updating of the thread field at all... :( – jinsungy Apr 09 '10 at 17:14
  • @jinsungy - With SQL Profiler, you should be able to select the statements that were executed which will compile them in the lower half of the screen. Copy and paste them into Management Studio. This will allow you to inject debugging code into results to test for the Thread column. – Thomas Apr 09 '10 at 17:21
  • The problem is the Update queries aren't found in the Profiler! – jinsungy Apr 09 '10 at 17:28
  • @jinsungy - It is probably only showing the stored procedure call. In profiler, click "Show All Events" and ensure that `SQL:StmtCompleted` is selected. This should show you all steps executed including those within the stored procs. – Thomas Apr 09 '10 at 18:41
  • OK, there may be nothing wrong with the code (loosk reasonable!) and it's the SCOPE_IDENTITY/parallelism bug. See my answer please – gbn Apr 10 '10 at 12:17

4 Answers4

4

You could be unlucky and hitting the (in)famous identity/parallelism bug reported on MS Connect, which is now a KB article too on both SQL Server 2005 and 2008.

That is, the value returned for SCOPE_IDENTITY is wrong. The code look OK and plenty of folk have had a look at it, so what if the value is wrong? This would give the same symptoms as you report.

Try OPTION (MAXDOP 1) on the insert as suggested in the articles

I've seen this happen myself so it's not abstract or rare.

gbn
  • 422,506
  • 82
  • 585
  • 676
2

"When you eliminate the impossible, whatever is left, however improbable, must be the truth"

There are only a handful of possibilities if we assume that there are no errors:

  1. The value is not being inserted or deleted. We can verify that by querying for it right after Set @ThreadId = SCOPE_IDENTITY()
  2. The Insert transaction is being rolled (e.g. from calling code transaction, from a calling sp transaction). We can verify that by looking at @@TRANCOUNT before and after the Update statement.
  3. Something is setting the Thread column to null right after your Update statement. We can check @@ROWCOUNT right after the Update statement. If it is zero, then the only possibility is that the record no longer exists. If it is 1, then clearly the update worked. Right after the Update statement, you should be able to call Select * From Messages Where MessageId = @ThreadId And Thread Is Not Null and get a record. That means if later in your code is Null again, something else had changed it.

Try the following:

Set NoCount Off

INSERT INTO [Messages]([Sender], [Receiver], [Job_Number], [Subject], [MessageText], [DateSent])

VALUES(@Sender, @Receiver, @Job_Number, @Subject, @MessageText, @DateSent)

SET @ThreadID = SCOPE_IDENTITY()

-- ensure that the value is not null
Select @ThreadId

-- we should get our record from this query
Select * From Messages Where MessageId = @ThreadId

UPDATE [Messages] 
SET Thread = @ThreadID
WHERE MessageID = @ThreadID

-- we should get 1
Select @@ROWCOUNT

-- we should get a value
Select * From Messages Where MessageId = @ThreadId And Thread Is Not Null

-- are we in a transaction?
Select @@TRANCOUNT

EDIT One other immensely helpful tool in rooting out these sorts of problems is the SQL Server Profiler. For example, you can tell it to show Rollback Tran completed or Commit Tran completed events along with the other SQL statements and see if something is rolling back the transaction.

Thomas
  • 63,911
  • 12
  • 95
  • 141
0

Check the Messages table for triggers.


SET @ThreadID = SCOPE_IDENTITY() 

PRINT convert(varchar(30), @ThreadID)

UPDATE [Messages]  
SET Thread = @ThreadID 
WHERE MessageID = @ThreadID 

Does it print what you expect?

Amy B
  • 108,202
  • 21
  • 135
  • 185
-1

I've seen this exact same problem before. The solution was to turn on NOCOUNT at the top of your stored procedure code (assuming it doesn't cause any unwanted side effects for you):

SET NOCOUNT ON

Alternatively, you can set nocount on from SQLHelper for the connection your using.

I don't have all the details about why this worked, other than when nocount is off (ie. rows are counted), it seems to "confuse" the following update statement (if I get more details I'll update my answer later). The same thing that you describe happened...the following UPDATE statement never executed, and even more so, the stored procedure unexpectedly exited (based on what was recorded in the SQL Profiler).

Joe L.
  • 1,888
  • 12
  • 14
  • 1
    Which (or both?) approach did you try? Adding SET NOCOUNT ON at top of stored proc, or setting it from your connection? – Joe L. Apr 09 '10 at 17:33
  • Just to clarify...so, you added SET NOCOUNT ON right before the INSERT INTO [Messages]... statement? Sorry, i'm not sure why that wouldn't work; your symptoms are seemingly identical ( and equally frustrating ;-) to what I've experienced. – Joe L. Apr 09 '10 at 17:48
  • Yes, I did exactly that. I've been working on this issue for a while now.. I will be changing the way I call the sproc. I am changing it to call it via LINQ. – jinsungy Apr 09 '10 at 17:55
  • Rubbish. SET NOCOUNT ON only affects DataAdaptors in the client and is irrelevant to subsequent processing in SQL code. See my question here http://stackoverflow.com/questions/1483732/set-nocount-on-usage – gbn Apr 11 '10 at 08:08
  • I agree (except for the rubbish part:-), the effect/problem only occurs on the client side (eg. DataAdaptors, ODBC connection, etc.). The problem doesn't occur when running the stored procedure on the server, eg. via Mgt Studio. – Joe L. Apr 13 '10 at 01:13