Is this safely unique?

T

Ted

As I don't have access to a GUID component on our shared ASP server, I need
to generate "unique" key for each order line in a basket table for an
eCommerce site.

I am using the following in Session_OnStart in the global.asa: -

session("UniqueOrderID") = session.sessionID &"D" & cdbl(Now())

which returns a value like 846912008D38092.7271296296.

Before I commit and program the site around this number, can anybody confirm
that this is a safe method to use?

Regards,

Ted.
 
A

Aaron Bertrand [MVP]

If it's in a table, why don't you just use IDENTITY / AUTONUMBER?
Guaranteed unique, and also much more efficient to pass from page to page
than that monstrosity you've created.
 
C

Chris Barber

ASP SessionIDs get reused so that part's not unique.

It is feasible to generate a GUID from a database or there are a couple of
methods to get one from ASP using:

http://www.ranainside.com/component_guid.html
http://www.techbase.iofm.net/tips/activeserver/1.html

Chris.

As I don't have access to a GUID component on our shared ASP server, I need
to generate "unique" key for each order line in a basket table for an
eCommerce site.

I am using the following in Session_OnStart in the global.asa: -

session("UniqueOrderID") = session.sessionID &"D" & cdbl(Now())

which returns a value like 846912008D38092.7271296296.

Before I commit and program the site around this number, can anybody confirm
that this is a safe method to use?

Regards,

Ted.
 
T

Ted

Thanks for the prompt reply Aaron. Please bear with me, as I am fairly new
to this eCommerce malarkey.

Here are the applicable relationships of my DB: -

CustomerTable ---< OrderTable ---<BasketItemsTable>---ProcuctTable

When the user adds an item to the basket, a record is created using my
UniqueID and ProductID as a composite primary key.
When the user views their basket, I use a select statement to grab all of
the basket lines from the basket table that correspond to the monstrosity,
or "unique order ID" for display.

When the user decides to place their order, I add a line to the order table
which links: -

Customer.CustomerID to OrderTable.CustomerID
and
OrderTable.UniqueOrderID to BasketItemsTable.UniqueOrderID

If I was to use your method, I would have to create a record in the
ordertable immediately upon the user visiting the site. And if the user
didn't place an order after visiting the site, I'd have a line in the order
table that was related to nothing, and would therefore have to set up some
procedure to remove those lines. Is this correct?

I have no idea about performance issues, but is a string session variable
less efficient than hitting the DB with a write? I am not expecting
overwhelming demand for the site. Does anyone know of any good resources for
performance issues? Each user will have 5 session variable that I deem
necessary to keep the DB hits down to a minimum. Oh, and I'll be hosting on
a shared server with MS SQL 2000 if that makes any difference.

Regards,

Ted.
 
A

Aaron Bertrand [MVP]

Why do you need an OrderID to track users before they place an order? You
already have a basketID, no?

Here's how I would do it:


CREATE TABLE [Product]
(
ProductID INT PRIMARY KEY
-- , other columns
)
GO

CREATE TABLE [Basket]
(
BasketID INT IDENTITY(1,1)
PRIMARY KEY,
CustomerID INT NULL
-- if you always know customerID
-- before the user adds an item,
-- you should make this a foreign
-- key to the Customer table
)
GO

CREATE TABLE [BasketItem]
(
BasketID INT
FOREIGN KEY
REFERENCES Basket(BasketID),
ProductID INT
FOREIGN KEY
REFERENCES Product(ProductID)
-- , other columns, e.g. quantity
)
GO

CREATE TABLE [Order]
(
OrderID INT IDENTITY(1,1),
BasketID INT
FOREIGN KEY
REFERENCES Basket(BasketID),
-- , other columns
PRIMARY KEY (OrderID, BasketID)
)
GO

Now, when a users adds an item to their basket, your ASP code looks like
this:

<%
sql = "EXEC addBasketItem " & productID & _
", " & CLng(session("basketID")) ' & ", <customerID>"
set rs = conn.execute(sql)
session("basketID") = rs(0)
%>

The stored procedure looks like this:

CREATE PROCEDURE dbo.addBasketItem
@productID INT,
@basketID INT = 0
-- , @customerID INT
-- , other parameters (e.g. quantity)
AS
BEGIN
SET NOCOUNT ON
IF @basketID = 0
(
-- if you know customerID
INSERT Basket(CustomerID) VALUES(@custmerID)
-- if you don't
INSERT Basket DEFAULT VALUES
SELECT @basketID = SCOPE_IDENTITY()
)
INSERT BasketItems
SELECT basketID = @basketID
END
GO

When you want to get the items in a person's basket:

<%
set rs = conn.execute("EXEC showMyBasket " & session("basketID"))
do while not rs.eof
... stuff
rs.movenext
loop
%>

The stored procedure:

CREATE PROCEDURE dbo.showMyBasket
@basketID INT
AS
BEGIN
SET NOCOUNT ON

SELECT p.ProductID --, p.othercolumns
, b.quantity
FROM Basket b
INNER JOIN Product p
ON b.ProductID = p.ProductID
WHERE b.basketID = @basketID
END
GO


Only when the user actually decides to place the order should you insert a
row into the orders table. All of the relationships you claim to require
before the order takes place are intact (e.g. customerID is in the basket,
assuming you know it beforehand), but aren't solidified until the point when
you actually need them to be.



Remember that your primary bottleneck is very unlikely to be whether you
choose a text session variable or an integer session variable. However
using an IDENTITY to produce your session variable is certainly going to be
more efficient, and less cumbersome, than your big string.

Also, at TechEd a couple of years ago, I had a lengthy discussion with an
engineer from Microsoft's IIS team, who told me (to my disbelief at the
time) that, everything else being equal, storing information in a database
is more efficient than keeping all that data in session variables. The
reason is that a database is optimized for storing data, while session
variables just gobble up memory. The busier your system gets, the more the
scales will tip towards database as the better performer. With a properly
tuned system, you will most likely yield better performance and more
manageable data with a database solution. In other words, test it, with
plausible real-world scenarios (not single user)... you shouldn't just opt
for session variables because you think they'll be more efficient than using
the database.

(I left out explicit declaration of clustered indexes, this will really
depend on how you are reporting against these tables and whether you are
doing more selects or inserts, etc.)
 
T

Ted

Wow, thanks for that comprehensive reply. Don't you have a job to do or
something? ;-)

Doesn't using your method create a one-to-one realtionship between the
Basket table and the Order table, which could otherwise be amalgamated into
one table?

Ted.
Why do you need an OrderID to track users before they place an order? You
already have a basketID, no?

Here's how I would do it:


CREATE TABLE [Product]
(
ProductID INT PRIMARY KEY
-- , other columns
)
GO

CREATE TABLE [Basket]
(
BasketID INT IDENTITY(1,1)
PRIMARY KEY,
CustomerID INT NULL
-- if you always know customerID
-- before the user adds an item,
-- you should make this a foreign
-- key to the Customer table
)
GO

CREATE TABLE [BasketItem]
(
BasketID INT
FOREIGN KEY
REFERENCES Basket(BasketID),
ProductID INT
FOREIGN KEY
REFERENCES Product(ProductID)
-- , other columns, e.g. quantity
)
GO

CREATE TABLE [Order]
(
OrderID INT IDENTITY(1,1),
BasketID INT
FOREIGN KEY
REFERENCES Basket(BasketID),
-- , other columns
PRIMARY KEY (OrderID, BasketID)
)
GO

Now, when a users adds an item to their basket, your ASP code looks like
this:

<%
sql = "EXEC addBasketItem " & productID & _
", " & CLng(session("basketID")) ' & ", <customerID>"
set rs = conn.execute(sql)
session("basketID") = rs(0)
%>

The stored procedure looks like this:

CREATE PROCEDURE dbo.addBasketItem
@productID INT,
@basketID INT = 0
-- , @customerID INT
-- , other parameters (e.g. quantity)
AS
BEGIN
SET NOCOUNT ON
IF @basketID = 0
(
-- if you know customerID
INSERT Basket(CustomerID) VALUES(@custmerID)
-- if you don't
INSERT Basket DEFAULT VALUES
SELECT @basketID = SCOPE_IDENTITY()
)
INSERT BasketItems
SELECT basketID = @basketID
END
GO

When you want to get the items in a person's basket:

<%
set rs = conn.execute("EXEC showMyBasket " & session("basketID"))
do while not rs.eof
... stuff
rs.movenext
loop
%>

The stored procedure:

CREATE PROCEDURE dbo.showMyBasket
@basketID INT
AS
BEGIN
SET NOCOUNT ON

SELECT p.ProductID --, p.othercolumns
, b.quantity
FROM Basket b
INNER JOIN Product p
ON b.ProductID = p.ProductID
WHERE b.basketID = @basketID
END
GO


Only when the user actually decides to place the order should you insert a
row into the orders table. All of the relationships you claim to require
before the order takes place are intact (e.g. customerID is in the basket,
assuming you know it beforehand), but aren't solidified until the point when
you actually need them to be.



Remember that your primary bottleneck is very unlikely to be whether you
choose a text session variable or an integer session variable. However
using an IDENTITY to produce your session variable is certainly going to be
more efficient, and less cumbersome, than your big string.

Also, at TechEd a couple of years ago, I had a lengthy discussion with an
engineer from Microsoft's IIS team, who told me (to my disbelief at the
time) that, everything else being equal, storing information in a database
is more efficient than keeping all that data in session variables. The
reason is that a database is optimized for storing data, while session
variables just gobble up memory. The busier your system gets, the more the
scales will tip towards database as the better performer. With a properly
tuned system, you will most likely yield better performance and more
manageable data with a database solution. In other words, test it, with
plausible real-world scenarios (not single user)... you shouldn't just opt
for session variables because you think they'll be more efficient than using
the database.

(I left out explicit declaration of clustered indexes, this will really
depend on how you are reporting against these tables and whether you are
doing more selects or inserts, etc.)

--
Aaron Bertrand
SQL Server MVP
http://www.aspfaq.com/


Ted said:
Thanks for the prompt reply Aaron. Please bear with me, as I am fairly new
to this eCommerce malarkey.

Here are the applicable relationships of my DB: -

CustomerTable ---< OrderTable ---<BasketItemsTable>---ProcuctTable

When the user adds an item to the basket, a record is created using my
UniqueID and ProductID as a composite primary key.
When the user views their basket, I use a select statement to grab all of
the basket lines from the basket table that correspond to the monstrosity,
or "unique order ID" for display.

When the user decides to place their order, I add a line to the order
table
which links: -

Customer.CustomerID to OrderTable.CustomerID
and
OrderTable.UniqueOrderID to BasketItemsTable.UniqueOrderID

If I was to use your method, I would have to create a record in the
ordertable immediately upon the user visiting the site. And if the user
didn't place an order after visiting the site, I'd have a line in the
order
table that was related to nothing, and would therefore have to set up some
procedure to remove those lines. Is this correct?

I have no idea about performance issues, but is a string session variable
less efficient than hitting the DB with a write? I am not expecting
overwhelming demand for the site. Does anyone know of any good resources
for
performance issues? Each user will have 5 session variable that I deem
necessary to keep the DB hits down to a minimum. Oh, and I'll be hosting
on
a shared server with MS SQL 2000 if that makes any difference.

Regards,

Ted.
 
A

Aaron Bertrand [MVP]

Wow, thanks for that comprehensive reply. Don't you have a job to do or
something? ;-)

Normally I would, but I'm on vacation right now. And I'm actually about to
embark on 8 days of drinks umbrellas and NO COMPUTER, if I can help it.
Doesn't using your method create a one-to-one realtionship between the
Basket table and the Order table, which could otherwise be amalgamated
into
one table?

Only for those baskets that end up becoming orders. Many people will add
items to their basket but not end up placing an order. You can write some
easy cleanup code to get rid of those on a daily or weekly basis, I suppose.
But it will keep your orders table (which has much more information than the
basket table) from getting polluted with "orders" that aren't really orders.
 
T

Ted

Thanks very much for the help. I think I've got my head round the issues
now.

I'm developing the DB in Access, and my hosting provider will "upsize" it to
MS SQL when it's complete. I'm going to have to do some research into the
Stored Procedures, as I want to get the site performing as well as I can
within the constraints of having to host on a shared server. It's a bit of a
blind science at the moment, as I have apsolutly no idea of what speeds to
expect.

Anyway, thanks again, and have a nice holiday.

Ted.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top