|
database
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
how do I prevent duplicate ID in my situationI 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. > 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... "Aaron Bertrand [SQL Server MVP]" wrote: Can I alter a column to use identity where there are already records in it? > > 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... > > > 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 :) ? > Can I alter a column to use identity where there are already records in Sure, the easiest way is to let Enterprise Manager do it for you. > it? 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 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. > Thanks for your help and insight. I can't resort to the Enterprise Manager How many times are you going to be relying on the clients to make a database > method because some of my clients don't have access to it. 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? > How many times are you going to be relying on the clients to make a database I have around 1000 clients because I am selling this as a prepackaged > 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? 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. > have access to. Don't get angry as I am just looking for a solution in my I don't know why you think anyone is angry. I'm more surprised that you > particular situation. 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... 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. Aaron Bertrand [SQL Server MVP] (ten.xoc@dnartreb.noraa) writes:
>> Can I alter a column to use identity where there are already records in Since Samuel has 1000 clients running in production, point-and-click>> 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... 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 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 :) ? 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 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. 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. 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. 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 > Samuel (programmer@nospam.nospam) writes:
> Thanks for your code first. I just posted a message not noticing yours No. Brian's code is correct. Instead of using SET TRANSACTION ISOLATION> 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): 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 Since you have a locking hint, it takes precedence of the general isolation> > Begin transaction > > SELECT @int_MaxID=Max(messageID) FROM messageTbl with (nolock); 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 > In fact, as far as I know, UPDLOCK is sufficient, as it implies HOLDLOCK I just want to clarify this: WITH(UPDLOCK, HOLDLOCK) is required in this > or serializable. 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 > Brian Selzer (br***@selzer-software.com) writes:
> I just want to clarify this: WITH(UPDLOCK, HOLDLOCK) is required in this Correct. However, in practice a single UPDLOCK works here as well under> 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. 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 > Correct. However, in practice a single UPDLOCK works here as well under I acknowledge your point, but I'm not convinced. There may be other issues > 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. 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 > Brian Selzer (br***@selzer-software.com) writes:
> I acknowledge your point, but I'm not convinced. There may be other Since transaction 2 reads the new max value, it has a lock on that row.> 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. 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 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 > 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! 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. 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? --CELKO-- (jcelko***@earthlink.net) writes:
> Let's get back to the basics of an RDBMS. Rows are not records; fields Why should he? You may get an itching feel when people violate 11179. > 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. 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 Good SQL programmers quickly identifies the cases where there is no> is what real SQL programmers do. Why did you immediately lapse into a > non-relational sequential file mode for this project? 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 Since Samuel apparently has been in the trade for a while, I hope that > professional integrity to do it right instead? 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 |
|||||||||||||||||||||||