> Here's a question I've been wrestling wtih.

> Suppose that there is an HTML form to create a new user.
> When the form
> is submitted, that user's information is added to the
> 'user' table.
> The table uses an auto-increment integer for its primary
> key/user id,
> so I'd like to grab that value for the newly created user.

> I've been using queries like this one: "SELECT
> MAX(user_id) FROM
> users", but I worry that if two users are created at the
> same time,
> then my query might give me the user id of the wrong user.

> Is there a better way to do this? Or maybe I shouldn't
> even worry about this?

> Thanks.
> Chris

I would definately worry about it.

Yes, if you "select max(user_id) from users" to get the inserted
record id after performing an insert without any other preparation,
you will create a race condition in which two pages could erroneously
receive the same user_id value even though 2 separate values had been
inserted into the table.

There are several ways to get around this issue.

One is to use an nvarchar, varchar or guid column in SQL server and
generate your ID prior to the insert. If you need to insert additional
records in related tables for the generated user, then you'll have to
either generate the ID in ColdFusion or you'll have to find another
way to return that GUID to CF from SQL Server (the function NewID() is
used in SQL Server to generate these id's) such as a stored procedure.
Oracle by comparison will give you SQL Standard Sequences (not widely
supported yet) which can be used similarly to identity columns but
allow you to fetch the next id prior to insert. Personally I prefer to
generate ID's in CF.

Another popular method is to use a cftransaction around the insert and
the select statements...

<cftransaction isolation="serializable">
  <cfquery ...>insert into ...</cfquery>
  <cfquery ...>select max() from ...</cfquery>
</cftransaction>

This eliminates the race condition at the expense of carving out some
scalability. By this I mean that as the size of your table becomes
larger, it takes longer to perform the select max() query. Generally
speaking the increase in time for the 2nd query isn't noticeable, but
it's possible in theory that it could be an issue with very large
numbers of records (I would guess in the millions, so it's not likely
to be an issue with a users table specifically, but may be with some
other tables).

Another option is to use database-specific tools to fetch the new id.
With SQL Server 2000 or 2005 you can use a single query with 2
statements like so:

<cfquery ...>
  insert into ...
  select scope_identity() as newid
</cfquery>

The scope_identity() function was added in SQL Server 2000 to resolve
the issue of the previously available @@identity variable not being
thread safe (i.e. same race condition issue you have now). Of course,
the trade here is that the code then becomes dependant on the choice
of SQL Server. Most of the time this is fine, but on the off chance
the company hires a new CTO who's in love with Oracle, you'll have to
replace any references to this method. Of course, the same is largely
true of the first method above if you use newid() to generate GUIDs,
although using stored procedures to perform those inserts can insulate
the ColdFusion code from those changes (i.e. you would still have to
do the work if you needed to convert it to use Oracle, but your CF
code would remain unchanged).

So -- there are some options, plus and minus. For my money it just
seems to be easiest in general to generate ID's in advance of the
insert with CF, although in some environments it's necessary to allow
records to be inserted by non CF systems as well, at which point a
database-specific tool (usually stored procedures) becomes a
necessity.


s. isaac dealey     434.293.6201
new epoch : isn't it time for a change?

add features without fixtures with
the onTap open source framework

http://www.fusiontap.com
http://coldfusion.sys-con.com/author/4806Dealey.htm


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
Message: http://www.houseoffusion.com/lists.cfm/link=i:4:234926
Archives: http://www.houseoffusion.com/cf_lists/threads.cfm/4
Subscription: http://www.houseoffusion.com/lists.cfm/link=s:4
Unsubscribe: 
http://www.houseoffusion.com/cf_lists/unsubscribe.cfm?user=11502.10531.4
Donations & Support: http://www.houseoffusion.com/tiny.cfm/54

Reply via email to