Home All Groups Group Topic Archive Search About

how do I prevent duplicate ID in my situation

Author
4 Nov 2005 7:21 PM
Samuel
Hi everyone,

I am running into a issue that only happens very rarely. In a web app I
developed (ASP.NET + SQL server), I need to add a record into a table
[messageTbl] that has a column [messageID] as the primary key, so I must
generate a unique value for this column (type int, not identity column).

This is a simplified version of the stored procedure (I am only showing how
I generate the messageID for a new record without other irrelevant code):

====
-- irrelevant code removed
declare @int_MaxID int
declare @int_newID int

SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);

SELECT @int_newID = ISNULL(@int_MaxID,0) + 1

Insert into messageTbl (messageID, .....) values (@int_newID, ....)
====

As you can see, I just get the max value + 1 as the new messageID. Now, when
my client use the application in a web server farm environment, he sometimes
get error like this:

/ Violation of PRIMARY KEY constraint 'messageid'. Cannot insert duplicate
key in object 'messageTbl'. The statement has been terminated. /

Since I am inserting the record right after I generate the new ID
(max(messageID)+1), is there anyway that I can lock the table for a very very
short time to prevent others to insert duplicate messageID?

Thanks for your help in advance.

Author
4 Nov 2005 7:26 PM
Aaron Bertrand [SQL Server MVP]
> As you can see, I just get the max value + 1 as the new messageID.

So, why not just use IDENTITY?  Then, collisions won't happen and you won't
have to worry about locking anything...
Author
4 Nov 2005 7:49 PM
Samuel
"Aaron Bertrand [SQL Server MVP]" wrote:

> > As you can see, I just get the max value + 1 as the new messageID.
>
> So, why not just use IDENTITY?  Then, collisions won't happen and you won't
> have to worry about locking anything...
>
>
>

Can I alter a column to use identity where there are already records in it?
I don't know how this can be done..

If I can't use identity when there is already records, can you share with me
some simple locking techniques :) ?
Author
4 Nov 2005 7:57 PM
Aaron Bertrand [SQL Server MVP]
> Can I alter a column to use identity where there are already records in
> it?

Sure, the easiest way is to let Enterprise Manager do it for you.
Right-click the table, Design Table, highlight the column in question and in
the bottom pane where it says Identity change the value to Yes.  Click the
Save button, and if the table is large, go get a coffee.

If you want to do it the hard way, you can script out what it's going to do,
but it's not very pretty at all...

A
Author
4 Nov 2005 8:30 PM
Samuel
Dear Aaron and Barry,

Thanks for your help and insight. I can't resort to the Enterprise Manager
method because some of my clients don't have access to it. I need to use a
programmatic way (like T-SQL script) that users can execute via ADO.NET to
change the database.

Barry, I have looked at the SET TRANSACTION ISOLATION LEVEL in the help, and
as I am relatively new to this area, I need you to confirm what I beleive is
true:

1) I will need to set the highest isolation level, i.e. SERIALIZABLE on the
messageTbl in my stored procedure since I dont want others to insert record?

2) Do I just re-code (from my original question) like:

declare @int_MaxID int
declare @int_newID int

SET TRANSACTION ISOLATION LEVEL Serializable

Begin transaction

SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);

SELECT @int_newID = ISNULL(@int_MaxID,0) + 1

Insert into messageTbl (messageID, .....) values (@int_newID, ....)

commit transaction
?

Thanks. I am still new to this so any insight on which level to choose will
be greatly appreciated.
Author
4 Nov 2005 8:39 PM
Aaron Bertrand [SQL Server MVP]
> Thanks for your help and insight. I can't resort to the Enterprise Manager
> method because some of my clients don't have access to it.

How many times are you going to be relying on the clients to make a database
change for you?  I don't think I would ever do that (at least not and still
warrant the product).  How many such clients do you need to make this change
for?  Why aren't YOU making the change?
Author
4 Nov 2005 8:46 PM
Samuel
> How many times are you going to be relying on the clients to make a database
> change for you?  I don't think I would ever do that (at least not and still
> warrant the product).  How many such clients do you need to make this change
> for?  Why aren't YOU making the change?

I have around 1000 clients because I am selling this as a prepackaged
software only, not a service. Only one or two clients have encountered this
issue. The clients installed the software on their own server that I dont
have access to. Don't get angry as I am just looking for a solution in my
particular situation.
Author
4 Nov 2005 8:52 PM
Aaron Bertrand [SQL Server MVP]
> have access to. Don't get angry as I am just looking for a solution in my
> particular situation.

I don't know why you think anyone is angry.  I'm more surprised that you
have 1000 of these out there, and only a couple of clients have had a
problem.  This seems like the kind of thing that should be discovered long
before something is shrinkwrapped and sold...
Author
4 Nov 2005 9:05 PM
Samuel
the reason only a couple of them ran into this problem is that the software
was not originally designed to fit into a clustered environment, and that
there is a lot more read than write to the database because of the purpose of
the software, making this problem a very very rare issue. They only
encountered it a couple of times even when there are more than 10,000 new
records per day for the past year.

Anyway, thanks for your comment.
Author
4 Nov 2005 11:27 PM
Erland Sommarskog
Aaron Bertrand [SQL Server MVP] (ten.xoc@dnartreb.noraa) writes:
>> Can I alter a column to use identity where there are already records in
>> it?
>
> Sure, the easiest way is to let Enterprise Manager do it for you.
> Right-click the table, Design Table, highlight the column in question
> and in the bottom pane where it says Identity change the value to Yes.
> Click the Save button, and if the table is large, go get a coffee.
>
> If you want to do it the hard way, you can script out what it's going to
> do, but it's not very pretty at all...

Since Samuel has 1000 clients running in production, point-and-click
stuff are out of the question.

And in any case, Enterprise Manager should *never* be used to change
tables directly. Always get the script, and review it carefully. There
are several serious problems in the change scripts generated by
Enterprise Manager. It may be OK for playing around in your development
database, but if you are to send out something to 1000 clients, you
need something which is a lot more robost and fool-proof than that
crap. (The EM script can be used as a boiler-plate though.)

Anyway, there is a much simpler solution that resorting to something as
complex as a table change: use transactions and the UPDLOCK hint, as
discussed elsewhere in the thread.


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
Author
4 Nov 2005 8:07 PM
JT
The easiest way to add an identity property to an existing integer column
would be through Enterprise Manager using the table designer. You would need
to specify the seed value to something greater than the existing the max id.
This involves copying the data to a temporary table, so for a large table it
may take a while to complete. Therefore, I would reccomend doing this during
system downtime.


Show quote
"Samuel" <programmer@nospam.nospam> wrote in message
news:B9509B41-965A-4D36-85E9-9E9C4E999F50@microsoft.com...
>
>
> "Aaron Bertrand [SQL Server MVP]" wrote:
>
>> > As you can see, I just get the max value + 1 as the new messageID.
>>
>> So, why not just use IDENTITY?  Then, collisions won't happen and you
>> won't
>> have to worry about locking anything...
>>
>>
>>
>
> Can I alter a column to use identity where there are already records in
> it?
> I don't know how this can be done..
>
> If I can't use identity when there is already records, can you share with
> me
> some simple locking techniques :) ?
Author
4 Nov 2005 7:50 PM
Barry
If you don't want to use IDENTITY (Which I can't think of reason why
you wouldn't in this scenario) look up
SET TRANSACTION ISOLATION LEVEL in the help.

HTH

Barry
Author
4 Nov 2005 10:45 PM
Samuel
Hi Barry,

I have looked at the SET TRANSACTION ISOLATION LEVEL in the help, and
as I am relatively new to this area, I need you to confirm what I beleive is
true:

1) I will need to set the highest isolation level, i.e. SERIALIZABLE on the
messageTbl in my stored procedure since I dont want others to insert record?

2) Do I just re-code (from my original question) like:

declare @int_MaxID int
declare @int_newID int

SET TRANSACTION ISOLATION LEVEL Serializable

Begin transaction

SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);

SELECT @int_newID = ISNULL(@int_MaxID,0) + 1

Insert into messageTbl (messageID, .....) values (@int_newID, ....)

commit transaction
?

Thanks. I am still new to this so any insight on which level to choose will
be greatly appreciated.
Author
5 Nov 2005 1:32 AM
Brian Selzer
Setting the transaction isolation level will not work.  See my response to
Erland's post.  You need to obtain update range-locks, or exclusive
range-locks during the SELECT and hold them until the INSERT has completed.
WITH(UPDLOCK, HOLDLOCK) within a transaction is the correct way to solve
your problem.

Show quote
"Samuel" <programmer@nospam.nospam> wrote in message
news:7B6843BA-ADF8-409A-B8FB-62E04FC11182@microsoft.com...
> Hi Barry,
>
> I have looked at the SET TRANSACTION ISOLATION LEVEL in the help, and
> as I am relatively new to this area, I need you to confirm what I beleive
> is
> true:
>
> 1) I will need to set the highest isolation level, i.e. SERIALIZABLE on
> the
> messageTbl in my stored procedure since I dont want others to insert
> record?
>
> 2) Do I just re-code (from my original question) like:
>
> declare @int_MaxID int
> declare @int_newID int
>
> SET TRANSACTION ISOLATION LEVEL Serializable
>
> Begin transaction
>
> SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);
>
> SELECT @int_newID = ISNULL(@int_MaxID,0) + 1
>
> Insert into messageTbl (messageID, .....) values (@int_newID, ....)
>
> commit transaction
> ?
>
> Thanks. I am still new to this so any insight on which level to choose
> will
> be greatly appreciated.
Author
4 Nov 2005 8:04 PM
Brian Selzer
DECLARE @int_MaxID INT SET @int_MaxID = NULL
BEGIN TRANSACTION
SELECT @int_MaxID = MAX(messageID) FROM messageTbl WITH(UPDLOCK, HOLDLOCK)
IF @@ERROR != 0 GOTO ERROR
SET @int_MaxID = ISNULL(@int_MaxID, 0) + 1
INSERT INTO messageTbl (messageID, ....) values (@int_MaxID, ....)
IF @@ERROR != 0 OR @@ROWCOUNT = 0 GOTO ERROR
COMMIT TRANSACTION
RETURN 0

ERROR:
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
RETURN -1

Show quote
"Samuel" <programmer@nospam.nospam> wrote in message
news:95F5CE1D-60F2-4091-8DF2-F83D5F0D3EEC@microsoft.com...
> Hi everyone,
>
> I am running into a issue that only happens very rarely. In a web app I
> developed (ASP.NET + SQL server), I need to add a record into a table
> [messageTbl] that has a column [messageID] as the primary key, so I must
> generate a unique value for this column (type int, not identity column).
>
> This is a simplified version of the stored procedure (I am only showing
> how
> I generate the messageID for a new record without other irrelevant code):
>
> ====
> -- irrelevant code removed
> declare @int_MaxID int
> declare @int_newID int
>
> SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);
>
> SELECT @int_newID = ISNULL(@int_MaxID,0) + 1
>
> Insert into messageTbl (messageID, .....) values (@int_newID, ....)
> ====
>
> As you can see, I just get the max value + 1 as the new messageID. Now,
> when
> my client use the application in a web server farm environment, he
> sometimes
> get error like this:
>
> / Violation of PRIMARY KEY constraint 'messageid'. Cannot insert duplicate
> key in object 'messageTbl'. The statement has been terminated. /
>
> Since I am inserting the record right after I generate the new ID
> (max(messageID)+1), is there anyway that I can lock the table for a very
> very
> short time to prevent others to insert duplicate messageID?
>
> Thanks for your help in advance.
Author
4 Nov 2005 8:42 PM
Samuel
Dear Brian,

Thanks for your code first. I just posted a message not noticing yours
first. If I read your code right, your code only locks the table when I
retrieve the max value, but not when I insert the new record. In other words,
other can still retrieve the same max value (I will use user A and B):

A - retrieve the max value = 255 (e.g.), and no others can retrieve at the
same time
B - comes in after A retrieves the value but before A inserts the record,
and gets the same max value
A - inserts the record with max value = 255
B - inserts the record with the same max value, and generates an error.

And this does not seem to solve the problem to me... am I reading it
correctly?

Also, can you comment on my code:

/* begin */

declare @int_MaxID int
declare @int_newID int

SET TRANSACTION ISOLATION LEVEL Serializable

Begin transaction

SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);

SELECT @int_newID = ISNULL(@int_MaxID,0) + 1

Insert into messageTbl (messageID, .....) values (@int_newID, ....)

commit transaction

/* end */

Thanks a lot!


Show quote
"Brian Selzer" wrote:

>
> DECLARE @int_MaxID INT SET @int_MaxID = NULL
> BEGIN TRANSACTION
> SELECT @int_MaxID = MAX(messageID) FROM messageTbl WITH(UPDLOCK, HOLDLOCK)
> IF @@ERROR != 0 GOTO ERROR
> SET @int_MaxID = ISNULL(@int_MaxID, 0) + 1
> INSERT INTO messageTbl (messageID, ....) values (@int_MaxID, ....)
> IF @@ERROR != 0 OR @@ROWCOUNT = 0 GOTO ERROR
> COMMIT TRANSACTION
> RETURN 0
>
> ERROR:
> IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
> RETURN -1
>
Author
4 Nov 2005 11:23 PM
Erland Sommarskog
Samuel (programmer@nospam.nospam) writes:
> Thanks for your code first. I just posted a message not noticing yours
> first. If I read your code right, your code only locks the table when I
> retrieve the max value, but not when I insert the new record. In other
> words, other can still retrieve the same max value (I will use user A
> and B):

No. Brian's code is correct. Instead of using SET TRANSACTION ISOLATION
LEVEL, he added to locking hints:

   (UPDLOCK, HOLDLOCK)

HOLDLOCK is the same as serializeable. This means that as long as the
transaction is not committed, no other user can insert a value that would
alter the result of the SELECT MAX query.

The UPDLOCK adds an extra twist to this: UPDLOCK is in itself only a shared
lock; other readers are not blocked. However, only one process at a time
can have UPDLOCK, so if two processes attempt to read with UPDLOCK, one
will be blocked. If the UPDLOCK had not been there, two simulataneous
processes would have read the MAX value, and then blocked each other from
the INSERT. That is, a deadlock.

In fact, as far as I know, UPDLOCK is sufficient, as it implies HOLDLOCK
or serializable.

You can actually try this for yourself in Query Analyzer, by running in
parallel windows. Use the WAITFOR command to add some delay so you get
the time to shift focus between the windows.

> SET TRANSACTION ISOLATION LEVEL Serializable
>
> Begin transaction
>
> SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);

Since you have a locking hint, it takes precedence of the general isolation
level. And NOLOCK or dirty reads are definitely not right.


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
Author
5 Nov 2005 1:27 AM
Brian Selzer
> In fact, as far as I know, UPDLOCK is sufficient, as it implies HOLDLOCK
> or serializable.

I just want to clarify this:  WITH(UPDLOCK, HOLDLOCK) is required in this
case.  THIS IS VERY IMPORTANT!!!

Assuming the transaction isolation level is less than SERIALIZABLE:

WITH(UPDLOCK) obtains an individual update lock which is held for the
duration of the transaction.
WITH(HOLDLOCK) obtains a shared range-lock which is held for the duration of
the transaction.
WITH(UPDLOCK, HOLDLOCK) obtains an update range-lock which is held for the
duration of the transaction.

In this case, a shared range-lock would cause deadlocks in a concurrent
environment because if the same procedure is executing on two unbound
connections, it is possible for shared range-locks to be obtained on the
same index, thus preventing the transition from shared locks to exclusive
locks from either connection.  An individual update lock doesn't solve the
problem posed because nothing prevents another transaction from inserting or
updating a row such that the key value is one more than the value just read
out, which can cause a primary key violation error.  An update range-lock is
indicated because it prevents the deadlock caused by current execution of
the same procedure and also prevents inserts with a key value larger than
the current max value.

If the transaction isolation level is set to SERIALIZABLE, you can drop the
HOLDLOCK, but I would advise against it since it's more clear from a
maintenance standpoint to explicitly specify the type of lock required.

Show quote
"Erland Sommarskog" <esq***@sommarskog.se> wrote in message
news:Xns97053AE4F1B8Yazorman@127.0.0.1...
> Samuel (programmer@nospam.nospam) writes:
>> Thanks for your code first. I just posted a message not noticing yours
>> first. If I read your code right, your code only locks the table when I
>> retrieve the max value, but not when I insert the new record. In other
>> words, other can still retrieve the same max value (I will use user A
>> and B):
>
> No. Brian's code is correct. Instead of using SET TRANSACTION ISOLATION
> LEVEL, he added to locking hints:
>
>   (UPDLOCK, HOLDLOCK)
>
> HOLDLOCK is the same as serializeable. This means that as long as the
> transaction is not committed, no other user can insert a value that would
> alter the result of the SELECT MAX query.
>
> The UPDLOCK adds an extra twist to this: UPDLOCK is in itself only a
> shared
> lock; other readers are not blocked. However, only one process at a time
> can have UPDLOCK, so if two processes attempt to read with UPDLOCK, one
> will be blocked. If the UPDLOCK had not been there, two simulataneous
> processes would have read the MAX value, and then blocked each other from
> the INSERT. That is, a deadlock.
>
> In fact, as far as I know, UPDLOCK is sufficient, as it implies HOLDLOCK
> or serializable.
>
> You can actually try this for yourself in Query Analyzer, by running in
> parallel windows. Use the WAITFOR command to add some delay so you get
> the time to shift focus between the windows.
>
>> SET TRANSACTION ISOLATION LEVEL Serializable
>>
>> Begin transaction
>>
>> SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);
>
> Since you have a locking hint, it takes precedence of the general
> isolation
> level. And NOLOCK or dirty reads are definitely not right.
>
>
> --
> Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se
>
> Books Online for SQL Server SP3 at
> http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
>
Author
5 Nov 2005 2:28 PM
Erland Sommarskog
Brian Selzer (br***@selzer-software.com) writes:
> I just want to clarify this:  WITH(UPDLOCK, HOLDLOCK) is required in this
> case.  THIS IS VERY IMPORTANT!!!
>...
> An individual update lock doesn't solve the problem posed because
> nothing prevents another transaction from inserting or updating a row
> such that the key value is one more than the value just read out, which
> can cause a primary key violation error. 

Correct. However, in practice a single UPDLOCK works here as well under
the assumption that all inserts are carried out through this routine,
and thus all inserters will be held up on the UPDLOCK no matter the
isolation level.

Of course, if INSERTS can come from all over the place, and some chooses
the PK value on their own free will, or even worse there are processes that
updates the PK, then this breaks down. But then again, those inserters and
updaters also have a risk for PK violation to consider.


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
Author
6 Nov 2005 4:38 AM
Brian Selzer
> Correct. However, in practice a single UPDLOCK works here as well under
> the assumption that all inserts are carried out through this routine,
> and thus all inserters will be held up on the UPDLOCK no matter the
> isolation level.

I acknowledge your point, but I'm not convinced.  There may be other issues
if there's a lot of activity.  SELECT MAX WITH(UPDLOCK) tries to obtain an
update lock on a single row.  Transaction 1 is granted the update row-lock,
and Transaction 2 blocks on that same row.  When Transaction 1 completes,
the update lock on the old max and the exclusive lock on the new max are
released.  Will Transaction 2 then succeed in locking the row with the old
max value or the one with the new max value?  What happens with more than 2
simultaneous transactions?  Can another transaction jump in line because the
second transaction was blocking on a row that is no longer the max value?
This uncertainty and confusion doesn't exist with an update range-lock.

Show quote
"Erland Sommarskog" <esq***@sommarskog.se> wrote in message
news:Xns97059D1161B68Yazorman@127.0.0.1...
> Brian Selzer (br***@selzer-software.com) writes:
>> I just want to clarify this:  WITH(UPDLOCK, HOLDLOCK) is required in this
>> case.  THIS IS VERY IMPORTANT!!!
>>...
>> An individual update lock doesn't solve the problem posed because
>> nothing prevents another transaction from inserting or updating a row
>> such that the key value is one more than the value just read out, which
>> can cause a primary key violation error.
>
> Correct. However, in practice a single UPDLOCK works here as well under
> the assumption that all inserts are carried out through this routine,
> and thus all inserters will be held up on the UPDLOCK no matter the
> isolation level.
>
> Of course, if INSERTS can come from all over the place, and some chooses
> the PK value on their own free will, or even worse there are processes
> that
> updates the PK, then this breaks down. But then again, those inserters and
> updaters also have a risk for PK violation to consider.
>
>
> --
> Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se
>
> Books Online for SQL Server SP3 at
> http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
>
Author
6 Nov 2005 10:36 AM
Erland Sommarskog
Brian Selzer (br***@selzer-software.com) writes:
> I acknowledge your point, but I'm not convinced.  There may be other
> issues if there's a lot of activity.  SELECT MAX WITH(UPDLOCK) tries to
> obtain an update lock on a single row.  Transaction 1 is granted the
> update row-lock, and Transaction 2 blocks on that same row.  When
> Transaction 1 completes, the update lock on the old max and the
> exclusive lock on the new max are released.  Will Transaction 2 then
> succeed in locking the row with the old max value or the one with the
> new max value?  What happens with more than 2 simultaneous transactions?
>  Can another transaction jump in line because the second transaction was
> blocking on a row that is no longer the max value? This uncertainty and
> confusion doesn't exist with an update range-lock.

Since transaction 2 reads the new max value, it has a lock on that row.

But you are perfectly right in that adding HOLDLOCK adds extra safety.
Possibly the range lock could have impact on concurrency, but I have not
been able to find such an example.


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
Author
6 Nov 2005 3:50 PM
Brian Selzer
The update range-lock only blocks changes from other unbound connections
that would cause a new max value to exist.  Reads won't block since shared
locks can still be obtained.  Therefore, any loss of concurrency is
necessary to maintain integrity.

There is one additional row added to syslockinfo for an update range-lock.
It's the one with rsc_text = (ffffffffffff).  This should have little if any
impact on performance, however.

Show quote
"Erland Sommarskog" <esq***@sommarskog.se> wrote in message
news:Xns970675D2DBF49Yazorman@127.0.0.1...
> Brian Selzer (br***@selzer-software.com) writes:
>> I acknowledge your point, but I'm not convinced.  There may be other
>> issues if there's a lot of activity.  SELECT MAX WITH(UPDLOCK) tries to
>> obtain an update lock on a single row.  Transaction 1 is granted the
>> update row-lock, and Transaction 2 blocks on that same row.  When
>> Transaction 1 completes, the update lock on the old max and the
>> exclusive lock on the new max are released.  Will Transaction 2 then
>> succeed in locking the row with the old max value or the one with the
>> new max value?  What happens with more than 2 simultaneous transactions?
>>  Can another transaction jump in line because the second transaction was
>> blocking on a row that is no longer the max value? This uncertainty and
>> confusion doesn't exist with an update range-lock.
>
> Since transaction 2 reads the new max value, it has a lock on that row.
>
> But you are perfectly right in that adding HOLDLOCK adds extra safety.
> Possibly the range lock could have impact on concurrency, but I have not
> been able to find such an example.
>
>
> --
> Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se
>
> Books Online for SQL Server SP3 at
> http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp
>
Author
5 Nov 2005 1:50 AM
Samuel
Thank you very much Erland for your instruction. I believe I have quite a lot
to digest in terms of transaction and lock hints. I am reading up on
documents about transactions and locks, and hopefully I will be able to
answer these questions myself the next time.

Thanks again!
Author
4 Nov 2005 8:09 PM
JT
Also, if this column should not be duplicated, then add a unique index or
constraint.

Show quote
"Samuel" <programmer@nospam.nospam> wrote in message
news:95F5CE1D-60F2-4091-8DF2-F83D5F0D3EEC@microsoft.com...
> Hi everyone,
>
> I am running into a issue that only happens very rarely. In a web app I
> developed (ASP.NET + SQL server), I need to add a record into a table
> [messageTbl] that has a column [messageID] as the primary key, so I must
> generate a unique value for this column (type int, not identity column).
>
> This is a simplified version of the stored procedure (I am only showing
> how
> I generate the messageID for a new record without other irrelevant code):
>
> ====
> -- irrelevant code removed
> declare @int_MaxID int
> declare @int_newID int
>
> SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock);
>
> SELECT @int_newID = ISNULL(@int_MaxID,0) + 1
>
> Insert into messageTbl (messageID, .....) values (@int_newID, ....)
> ====
>
> As you can see, I just get the max value + 1 as the new messageID. Now,
> when
> my client use the application in a web server farm environment, he
> sometimes
> get error like this:
>
> / Violation of PRIMARY KEY constraint 'messageid'. Cannot insert duplicate
> key in object 'messageTbl'. The statement has been terminated. /
>
> Since I am inserting the record right after I generate the new ID
> (max(messageID)+1), is there anyway that I can lock the table for a very
> very
> short time to prevent others to insert duplicate messageID?
>
> Thanks for your help in advance.
Author
6 Nov 2005 2:35 AM
--CELKO--
Let's get back to the basics of an RDBMS. Rows are not records; fields
are not columns; tables are not files.   Can you tell me why the
postfix "-tbl" is not a redundant as well as a violation of ISO-11179.


Did you once even ask what the natural key of a message might be? That
is what real SQL programmers do. Why did you immediately lapse into a
non-relational sequential file mode for this project?

Perhaps a sender id and UTC timestamp, which you would put into the
database anyway?  Should the messages be put into a tree like this
site? Etc.

You are asking for kludges and you will get them.  Do you have the
professional integrity to do it right instead?
Author
6 Nov 2005 10:42 AM
Erland Sommarskog
--CELKO-- (jcelko***@earthlink.net) writes:
> Let's get back to the basics of an RDBMS. Rows are not records; fields
> are not columns; tables are not files.   Can you tell me why the
> postfix "-tbl" is not a redundant as well as a violation of ISO-11179.

Why should he? You may get an itching feel when people violate 11179.
Most others don't give a single penny for it. Were you on the committe
and want the royalties or what?

> Did you once even ask what the natural key of a message might be? That
> is what real SQL programmers do. Why did you immediately lapse into a
> non-relational sequential file mode for this project?

Good SQL programmers quickly identifies the cases where there is no
natural key - those cases are abundant. I don't know Samuels application,
but a message sounds like something that may not have a natural key.
For instance the message id of your message was:
<1131244524.012913.80***@f14g2000cwb.googlegroups.com>

How you think Google generated this message ID? Did they get your shoe
size and your other data and generated a natural key from that?

> You are asking for kludges and you will get them.  Do you have the
> professional integrity to do it right instead?

Since Samuel apparently has been in the trade for a while, I hope that
Samuel has enough professional integrity to ignore your hostile ramblings.


--
Erland Sommarskog, SQL Server MVP, esq***@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp

AddThis Social Bookmark Button